Upgrading - Upgrading to 0.19

In the Liveblocks 0.19 release, we’re adding support for Zustand v4 to @liveblocks/zustand. Zustand v4 brings greatly improved TypeScript types to its APIs, enabling us to enhance the quality of our types, bringing it in line with our React package.

Let’s take a look!

Upgrading steps by package

@liveblocks/react

To update @liveblocks/react to 0.19, run the following command using your preferred package manager:

$npm install @liveblocks/client@0.19 @liveblocks/react@0.19

useOther now requires a selector argument

useOther now requires a selector function argument. You will need to replace instances of useOther that do not use a selector in your codebase.

Previously ❌

const other = useOther(id); // 👈 does not include a selector

Now ✅

const other = useOther(id, (other) => other); // 👈 requires a selector

Include unstable_batchedUpdates if you use React 17

We’ve added support to prevent the stale props/zombie child scenario. To avoid this issue, we enforce passing the unstable_batchedUpdates prop to RoomProvider.

// ⚠️ Only if you’re using React 17 or lowerimport { unstable_batchedUpdates } from "react-dom"; // 👈<RoomProvider  id="my-room"  initialPresence={...}  initialStorage={...}  unstable_batchedUpdates={unstable_batchedUpdates}>  <App /></RoomProvider>

For additional context, see the troubleshooting guide

@liveblocks/redux

To update @liveblocks/redux to 0.19, run the following command using your preferred package manager:

$npm install @liveblocks/client@0.19 @liveblocks/redux@0.19

Update use of default export liveblocksEnhancer

The main export has been renamed, so you will need to update your imports and use of the enhancer:

Previously ❌

import { enhancer } from "@liveblocks/redux";

Now ✅

import { liveblocksEnhancer } from "@liveblocks/redux";

Remove the second argument to state.liveblocks.enterRoom

When calling state.liveblocks.enterRoom(), you should not pass an explicit initial state. It will use the state in your Redux store, for consistency and ease of use. To migrate, make the following code changes:

Previously ❌

useEffect(() => {  enterRoom("room-id", {    todos: [], // 👈 remove explicit initial state  });});

Now ✅

useEffect(() => {  enterRoom("room-id");});

@liveblocks/zustand

In 0.19 we added support for Zustand v4 (specifically v4.1.3 or higher) and will no longer support Zustand v4.1.2 or lower. This is because Zustand v4.1.3 brings greatly improved TypeScript types to its APIs, and consequently, we can improve our internal types. To migrate, make the following code changes:

To update @liveblocks/zustand to 0.19, run the following command using your preferred package manager:

$npm install @liveblocks/client@0.19 @liveblocks/zustand@0.19
  • Change these imports, if applicable, and rename accordingly:

Previously ❌

import { middleware } from "@liveblocks/zustand";import type { LiveblocksState } from "@liveblocks/zustand";

Now ✅

import { liveblocks } from "@liveblocks/zustand";import type { WithLiveblocks } from "@liveblocks/zustand";
  • Update to the Zustand v4 recommended pattern:

Previously ❌

create(liveblocks<MyState, ...>(...))

Now ✅

create<WithLiveblocks<MyState, ...>>()(liveblocks(...))

To be clear:

  1. First, move the type annotation away from the liveblocks middleware call, and onto the create call.
  2. Next, wrap your MyState type in a WithLiveblocks<...> wrapper. This will make sure the injected liveblocks property on your Zustand state will be correctly typed.
  3. Finally, make sure to add the extra call () wrapper, needed by Zustand v4 now:
    create<WithLiveblocks<MyState, ...>>()(liveblocks(...))//                                  ^^ Not a typo
  • Remove the second argument to state.liveblocks.enterRoom(): it no longer takes an explicit initial state. Instead, it’s automatically be populated from your Zustand state.

Improvements

This release brings several changes to @liveblocks/react, which improve rendering performance and stability. Additionally, we have refactored our internal packages to increase code sharing. You can review the release notes for more details.

@liveblocks/react

New shouldInitiallyConnect prop

We added a new property shouldInitiallyConnect to RoomProvider, which lets you control whether or not the room connects to Liveblock servers. By default, it will check the typeof window to determine if it should connect. When using SSR, you can set it to false to prevent the room from connecting to Liveblocks servers.

<RoomProvider  id="room"  initialPresence={{}}  shouldInitiallyConnect={false} // 👈 control initial connection to Liveblocks>  <App /></RoomProvider>

Addition of @liveblocks-core package

We restructured our internal packages to increase code sharing. You may notice a new dependency in your dependency tree: @liveblocks/core. It contains private APIs that aren’t intended for direct consumption.

@liveblocks/client

New shouldInitiallyConnect option

Similar to the shouldInitiallyConnect prop in @liveblocks/react, we added a shouldInitiallyConnect option to client.enter. The default value is true. You may want to set it to false when using SSR, or if you would prefer to establish the WebSocket connection later.

client.enter("room", {  initialPresence: {},  shouldInitiallyConnect: false, // 👈 for SSR using the @liveblocks/client package});

@liveblocks/*

All packages now provide an isReadOnly flag on user instances. It is available by calling getSelf() and getOthers. isReadOnly is true when storage is read-only, as well as when a user has read permission to the room and write permission to presence.

const me = room.getSelf();
me.isReadOnly; // boolean
const others = room.getOthers();for (const other of others) { other.isReadOnly; // boolean}

You can learn more about room permissions under authentication.

That’s it!

If you run into issues with these new patterns and you need help, please let us know by email or by joining our Discord community! We’re here to help!