Today, we’re launching a new feature that enables developers to initialize their
Liveblocks config files with a single command.

## Configuration files

Liveblocks allows you to place all your TypeScript types in a single config
file, `liveblocks.config.ts`, and have them propagate throughout your
application. This is made possible by passing your types to the
`createRoomContext` function for React.

```ts file="liveblocks.config.ts"
type Presence = {
  cursor: { x: number; y: number } | null;
};

export const { useMyPresence } = createRoomContext<Presence>(client);
```

After adding this, you can then export all your hooks from
`liveblocks.config.ts`, and have automatic typing inside your app.

```ts file="App.ts"
import { useMyPresence } from "./liveblocks.config.ts";

// ❌ Generic never necessary, e.g. `<Presence>`
const [myPresence, updateMyPresence] = useMyPresence<Presence>();

// ✅ Automatic types throughout
const [myPresence, updateMyPresence] = useMyPresence();
```

## Generating the file

Previously, engineers had to write `liveblocks.config.ts` files manually before
integrating Liveblocks into the core of their applications. This made onboarding
slower than it needed to be, especially for people that wanted to try Liveblocks
for the first time.

It now only takes a few seconds to generate a full `liveblocks.config.ts` file
by running the following command in your application folder.

```
npx create-liveblocks-app@latest --init
```

Here’s what running that command looks like for a React Suspense configuration.

<Figure>
  <video autoPlay loop muted playsInline>
    <source
      src="/images/blog/initialize-config-command/liveblocks-initialize-config-terminal.mp4"
      type="video/mp4"
    />
  </video>
</Figure>

This will generate the following `liveblocks.config.ts` file automatically for
you, enabling you to quickly start building multiplayer experiences for your
product.

```ts file="liveblocks.config.ts"
import { createClient } from "@liveblocks/client";
import { createRoomContext } from "@liveblocks/react";

const client = createClient({
  // publicApiKey: "",
  // authEndpoint: "/api/auth",
  // throttle: 100,
});

// Presence represents the properties that will exist on every User in the Room
// and that will automatically be kept in sync. Accessible through the
// `user.presence` property. Must be JSON-serializable.
type Presence = {
  // cursor: { x: number, y: number } | null,
  // ...
};

// Optionally, Storage represents the shared document that persists in the
// Room, even after all Users leave. Fields under Storage typically are
// LiveList, LiveMap, LiveObject instances, for which updates are
// automatically persisted and synced to all connected clients.
type Storage = {
  // author: LiveObject<{ firstName: string, lastName: string }>,
  // ...
};

// Optionally, UserMeta represents static/readonly metadata on each User, as
// provided by your own custom auth backend (if used). Useful for data that
// will not change during a session, like a User's name or avatar.
// type UserMeta = {
//   id?: string,  // Accessible through `user.id`
//   info?: Json,  // Accessible through `user.info`
// };

// Optionally, the type of custom events broadcast and listened to in this
// room. Must be JSON-serializable.
// type RoomEvent = {};

export const {
  suspense: {
    RoomProvider,
    useRoom,
    useMyPresence,
    useUpdateMyPresence,
    useSelf,
    useOthers,
    useOthersMapped,
    useOthersConnectionIds,
    useOther,
    useBroadcastEvent,
    useEventListener,
    useErrorListener,
    useStorage,
    useObject,
    useMap,
    useList,
    useBatch,
    useHistory,
    useUndo,
    useRedo,
    useCanUndo,
    useCanRedo,
    useMutation,
  },
} = createRoomContext<Presence, Storage /* UserMeta, RoomEvent */>(client);
```

You can then start using the Liveblocks `RoomProvider` and hooks to build your
realtime app, such as a collaborative editor. For instance, here’s the React
code for listing the `connectionId` of everyone currently connected to the same
room.

```tsx file="index.tsx"
import { ClientSideSuspense } from "@liveblocks/react";
import { RoomProvider, useOthers, useSelf } from "./liveblocks.config";

export default function Page() {
  return (
    <RoomProvider id="my-collaborative-room" initialPresence={{}}>
      <ClientSideSuspense fallback="Loading…">
        {() => <CollaborativeEditor />}
      </ClientSideSuspense>
    </RoomProvider>
  );
}

function CollaborativeEditor() {
  const users = useOthers();
  const currentUser = useSelf();

  return (
    <ul>
      {users.map(({ connectionId }) => {
        return <li key={connectionId}>{connectionId}</li>;
      })}
    </ul>
  );
}
```

## Updating your config for live cursors

One of the simplest use cases for Liveblocks is live cursors, and how is how you
can integrate it into your React application. In most scenarios, you’d need to
adjust the `Storage`, `Presence`, `RoomEvent`, and `UserMeta` types to fit your
needs, but live cursors only needs `Presence`, which could look like this.

```ts file="liveblocks.config.ts"
type Presence = {
  cursor: { x: number; y: number } | null;
};
```

You can then update people’s cursor positions with `updateMyPresence` from the
`useMyPresence` hook. To display other people’s cursors, simply loop through the
response from `useOthers` and display a custom React component that renders SVG
cursors with the appropriate X and Y coordinates.

```tsx file="index.tsx"
function CollaborativeEditor() {
  const users = useOthers();
  const updateMyPresence = useUpdateMyPresence();

  return (
    <div
      onPointerMove={(event) => {
        updateMyPresence({
          cursor: {
            x: Math.round(event.clientX),
            y: Math.round(event.clientY),
          },
        });
      }}
      onPointerLeave={() =>
        updateMyPresence({
          cursor: null,
        })
      }
    >
      {users.map(({ connectionId, presence }) => {
        if (presence.cursor === null) {
          return null;
        }

        return (
          <Cursor
            key={connectionId}
            x={presence.cursor.x}
            y={presence.cursor.y}
          />
        );
      })}
    </div>
  );
}
```

That’s all you need—you now have live cursors in your React application!

<Figure>
  <video autoPlay loop muted playsInline>
    <source
      src="/images/blog/initialize-config-command/liveblocks-live-cursors.mp4"
      type="video/mp4"
    />
  </video>
</Figure>

---

If you’d like to make your product multiplayer like Figma, Notion, and others,
feel free to [create an account](/auth/signup), [read our docs](/docs) or skim
through our [GitHub repository](http://github.com/liveblocks/liveblocks). We
can’t wait to see what you make with Liveblocks!