@liveblocks/react
provides you with React bindings for
our realtime collaboration APIs, built on top of WebSockets. Read our
getting started guides to learn more.
All Liveblocks React components and hooks can be exported from two different
locations, @liveblocks/react/suspense
and @liveblocks/react
. This is because
Liveblocks provides two types of hooks; those that support
React Suspense, and those that
don’t.
We recommend importing from @liveblocks/react/suspense
and using Suspense by
default, as it often makes it easier to build your collaborative application.
Suspense hooks can be wrapped in ClientSideSuspense
, which acts as a
loading spinner for any components below it. When using this, all components
below will only render once their hook contents have been loaded.
Advanced hooks using the { ..., error, isLoading }
syntax, such as
useThreads
, can also use
ErrorBoundary
to render an
error if the hook runs into a problem.
An advantage of Suspense hooks is that you can have multiple different hooks in
your tree, and you only need a single ClientSideSuspense
component to render a
loading spinner for all of them.
Regular hooks often return null
whilst a component is loading, and you must
check for this to render a loading spinner.
Advanced hooks using the { ..., error, isLoading }
syntax, such as
useThreads
, require you to make sure there isn’t a problem before using
the data.
Liveblocks provides a component named ClientSideSuspense
which works as a
replacement for Suspense
. This is helpful as our Suspense hooks will throw an
error when they’re run on the server, and this component avoids this issue by
always rendering the fallback
on the server.
Instead of wrapping your entire Liveblocks application inside a single
ClientSideSuspense
component, you can use multiple of these components in
different parts of your application, and each will work as a loading fallback
for any components further down your tree.
This is a great way to build a static skeleton around your dynamic collaborative application.
Sets up a client for connecting to Liveblocks, and is the recommended way to
create to do this for React apps. You must define either authEndpoint
or
publicApiKey
. Resolver functions should be placed inside here, and a number of
other options are available, which correspond with those passed to
createClient
. Unlike RoomProvider
, LiveblocksProvider
doesn’t call
Liveblocks servers when mounted, and it should be placed higher in your app’s
component tree.
When creating a client with a public key, you don’t need to set up an authorization endpoint. We only recommend using a public key when prototyping, or on public landing pages, as it makes it possible for end users to access any room’s data. You should instead use an auth endpoint.
If you are not using a public key, you need to set up your own authEndpoint
.
Please refer to our Authentication guide.
If you need to add additional headers or use your own function to call your
endpoint, authEndpoint
can be provided as a custom callback. You should return
the token created with
Liveblocks.prepareSession
or liveblocks.identifyUser
,
learn more in authentication guide.
room
is the room ID that the user is connecting to. When using
Notifications, room
can be
undefined
, as the client is requesting a token that grants access to multiple
rooms, rather than a specific room.
Here’s an example of fetching your API endpoint at /api/liveblocks-auth
within
the callback.
You should return the token created with
Liveblocks.prepareSession
or liveblocks.identifyUser
.
These are the values the functions can return.
{ "token": "..." }
shaped response.{ "error": "forbidden", "reason": "..." }
shaped response. If this is
returned, the client will disconnect and won't keep trying to authorize.Any other error will be treated as an unexpected error, after which the client will retry the request until it receives either 1. or 2.
By default, the client throttles the WebSocket messages sent to one every 100
milliseconds, which translates to 10 updates per second. It’s possible to
override that configuration with the throttle
option with a value between 16
and 1000
milliseconds.
This option is helpful for smoothing out realtime animations in your application, as you can effectively increase the framerate without using any interpolation. Here are some examples with their approximate frames per second (FPS) values.
If you’re connected to a room and briefly lose connection, Liveblocks will reconnect automatically and quickly. However, if reconnecting takes longer than usual, for example if your network is offline, then the room will emit an event informing you about this.
How quickly this event is triggered can be configured with the
lostConnectionTimeout
setting, and it takes a number in milliseconds.
lostConnectionTimeout
can be set between 1000
and 30000
milliseconds. The
default is 5000
, or 5 seconds.
You can listen to the event with useLostConnectionListener
. Note that this
also affects when others
are reset to an empty array after a disconnection.
This helps prevent temporary flashes in your application as a user quickly
disconnects and reconnects. For a demonstration of this behavior, see our
connection status example.
By default, Liveblocks applications will maintain an active WebSocket connection
to the Liveblocks servers, even when running in a browser tab that’s in the
background. However, if you’d prefer for background tabs to disconnect after a
period of inactivity, then you can use backgroundKeepAliveTimeout
.
When backgroundKeepAliveTimeout
is specified, the client will automatically
disconnect applications that have been in an unfocused background tab for at
least the specified time. When the browser tab is refocused, the client will
immediately reconnect to the room and synchronize the document.
backgroundKeepAliveTimeout
accepts a number in milliseconds—we advise using a
value of at least a few minutes, to avoid unnecessary disconnections.
Comments stores user IDs in its system, but no other user information. To display user information in Comments components, such as a user’s name or avatar, you need to resolve these IDs into user objects. This function receives a list of user IDs and you should return a list of user objects of the same size, in the same order.
The name and avatar you return are rendered in
Thread
components.
The user objects returned by the resolver function take the shape of
UserMeta["info"]
, which contains name
and avatar
by default. These two
values are optional, though if you’re using the
Comments default components,
they are necessary. Here’s an example of userIds
and the exact values
returned.
You can also return custom information, for example, a user’s color
:
You can access any values set within resolveUsers
with the
useUser
hook.
When using Notifications with
Comments, room IDs will be used to contextualize
notifications (e.g. “Chris mentioned you in room-id”) in the
InboxNotification
component. To replace room IDs with more fitting names (e.g. document names,
“Chris mentioned you in Document A”), you can provide a resolver function to
the resolveRoomsInfo
option in LiveblocksProvider
.
This resolver function will receive a list of room IDs and should return a list of room info objects of the same size and in the same order.
In addition to the room’s name, you can also provide a room’s URL as the url
property. If you do so, the
InboxNotification
component will automatically use it. It’s possible to use an inbox
notification’s roomId
property to construct a room’s URL directly in React and
set it on
InboxNotification
via href
, but the room ID might not be enough for you to construct the URL ,
you might need to call your backend for example. In that case, providing it via
resolveRoomsInfo
is the preferred way.
To enable creating mentions in Comments, you can
provide a resolver function to the resolveMentionSuggestions
option in
LiveblocksProvider
. These mentions will be displayed in
the Composer
component.
This resolver function will receive the mention currently being typed (e.g. when
writing “@jane”, text
will be jane
) and should return a list of user IDs
matching that text. This function will be called every time the text changes but
with some debouncing.
To use @liveblocks/client
in Node.js, you need to provide WebSocket
and
fetch
polyfills. As polyfills, we recommend installing ws
and
node-fetch
.
Then, pass them to the LiveblocksProvider
polyfill option as below.
Note that node-fetch
v3+
does not support CommonJS.
If you are using CommonJS, downgrade node-fetch
to v2.
To use @liveblocks/client
with React Native, you
need to add an atob
polyfill. As a polyfill, we recommend installing
base-64
.
Then you can pass the decode
function to our atob
polyfill option when you
create the client.
Creates a LiveblocksProvider
and a set of typed hooks. Note that any
LiveblocksProvider
created in this way takes no props, because it uses
settings from the client
instead. We recommend using it in
liveblocks.config.ts
and re-exporting your typed hooks like below.
While createRoomContext
offers APIs for interacting with
rooms (e.g. Presence, Storage, and Comments),
createLiveblocksContext
offers APIs for
interacting with Liveblocks features that are not tied to a specific room (e.g.
Notifications).
Returns the client
of the
nearest LiveblocksProvider
above in the React component tree.
Makes a Room
available in the component hierarchy below. Joins the room
when the component is mounted, and automatically leaves the room when the
component is unmounted. When using
Realtime APIs, initial Presence values for each
user, and Storage values for the room can be set.
Presence is used for storing temporary user-based values, such as a user’s
cursor coordinates, or their current selection. Each user has their own
presence, and this is readable for all other connected users. Set your initial
Presence value by using initialPresence
.
Each user’s Presence resets every time they disconnect, as this is only meant
for temporary data. Any JSON-serializable object is allowed (the JsonObject
type).
Storage is used to store permanent data that’s used in your application, such as
shapes on a whiteboard, nodes on a flowchart, or text in a form. The first time
a room is entered, you can set an initial value by using initialStorage
. Note
that this value is only read a single time.
Any
conflict-free live structures
and JSON-serializable objects are allowed (the LsonObject
type).
Creates a RoomProvider
and a set of typed hooks to use in your app. Note
that any RoomProvider
created in this way does not need to be nested in
LiveblocksProvider
, as it already has access to the client
. We generally
recommend typing your app using the newer method instead. When using
createRoomContext
it can be helpful to use it in liveblocks.config.ts
and
re-export your typed hooks as below.
To use the React suspense version of our hooks with createRoomContext
, you can
export from the suspense
property instead.
To type your hooks, you can pass multiple different types to
createRoomContext
. A full explanation is in the code snippet below.
Returns the Room
of the nearest RoomProvider
above in the React
component tree.
Returns a boolean, true
if the hook was called inside a
RoomProvider
context, and
false
otherwise.
useIsInsideRoom
is helpful for rendering different components depending on
whether they’re inside a room, or not. One example is a header component that
only displays a live avatar stack when users are connected to the room.
Here’s how the example above would render in three different
LiveblocksProvider
and RoomProvider
contexts.
Listen to potential room connection errors.
Returns the current WebSocket connection status of the room, and will re-render your component whenever it changes.
The possible value are: initial
, connecting
, connected
, reconnecting
, or
disconnected
.
Returns the current storage status of the room, and will re-render your
component whenever it changes. A { smooth: true }
option is also available,
which prevents quick changes between states, making it ideal for
rendering a synchronization badge in your app.
👉 A Suspense version of this hook is also available.
Passing { smooth: true }
prevents the status changing from "synchronizing"
to "synchronized"
until 1 second has passed after the final change. This means
it’s ideal for rendering a Storage status badge, as it won’t flicker in a
distracting manner when changes are made in quick succession.
Storage usually synchronizes milliseconds after a change, but in some circumstances, such as a user with a poor connection, it may take longer before they receive a response from our servers. It can be helpful to check if Storage changes have been synchronized, and prevent users from leaving the page if they haven’t.
It’s possible to implement this with useStorageStatus
, however with
useRoom
you can create a
version that renders much less frequently, so we recommend using this instead.
Calls the given callback when an “others” event occurs. Possible event types are:
enter
– A user has entered the room.leave
– A user has left the room.reset
– The others list has been emptied. This is the first event that
occurs when the room is entered. It also occurs when you’ve lost connection to
the room.update
– A user’s presence data has been updated.Calls the given callback in the exceptional situation that a connection is lost and reconnecting does not happen quickly enough.
This event allows you to build high-quality UIs by warning your users that the app is still trying to re-establish the connection, for example through a toast notification. You may want to take extra care in the mean time to ensure their changes won’t go unsaved.
When this happens, this callback is called with the event lost
. Then, once the
connection restores, the callback will be called with the value restored
. If
the connection could definitively not be restored, it will be called with
failed
(uncommon).
The lostConnectionTimeout
client option will determine how quickly this
event will fire after a connection loss (default: 5 seconds).
Automatically unsubscribes when the component is unmounted.
For a demonstration of this behavior, see our connection status example.
Return the presence of the current user, and a function to update it. Automatically subscribes to updates to the current user’s presence.
Note that the updateMyPresence
setter function is different to the setter
function returned by React’s useState
hook. Instead, you can pass a partial
presence object to updateMyPresence
, and any changes will be merged into the
current presence. It will not replace the entire presence object.
This is roughly equal to:
updateMyPresence
accepts an optional argument to add a new item to the
undo/redo stack. See room.history
for more information.
Returns a setter function to update the current user’s presence.
Use this if you don’t need the current user’s presence in your component, but
you need to update it (e.g. live cursor). It’s better to use
useUpdateMyPresence
because it won’t subscribe your component to get
rerendered when the presence updates.
Note that the updateMyPresence
setter function is different to the setter
function returned by React’s useState
hook. Instead, you can pass a partial
presence object to updateMyPresence
, and any changes will be merged into the
current presence. It will not replace the entire presence object.
updateMyPresence
accepts an optional argument to add a new item to the
undo/redo stack. See room.history
for more information.
Returns the current user once it is connected to the room, and automatically subscribes to updates to the current user.
The benefit of using a selector is that it will only update your component if that particular selection changes. For full details, see how selectors work.
👉 A Suspense version of this hook is also available, which will never return
null
.
Extracts data from the list of other users currently in the same Room, and automatically subscribes to updates on the selected data. For full details, see how selectors work.
The others
argument to the useOthers
selector function is an immutable
array of Users.
👉 A Suspense version of this hook is also available, which will never return
null
.
One caveat with this API is that selecting a subset of data for each user
quickly becomes tricky. When you want to select and get updates for only a
particular subset of each user’s data, we recommend using the
useOthersMapped
hook instead, which is optimized for this use case.
When called without arguments, returns the user list and updates your component whenever anything in it changes. This might be way more often than you want!
Extract data using a selector for every user in the room, and subscribe to all changes to the selected data. A Suspense version of this hook is also available.
The key difference with useOthers
is that the selector (and the optional
comparison function) work at the item level, like doing a .map()
over the
others array.
Returns an array where each item is a pair of [connectionId, data]
. For
pragmatic reasons, the results are keyed by the connectionId
, because in most
cases you’ll want to iterate over the results and draw some UI for each, which
in React requires you to use a key={connectionId}
prop.
Returns an array of connection IDs (numbers), and rerenders automatically when
users join or leave. This hook is useful in particular in combination with the
useOther
(singular) hook, to implement high-frequency rerendering of
components for each user in the room, e.g. cursors. See the useOther
(singular) documentation below for a full usage example.
Roughly equivalent to:
👉 A Suspense version of this hook is also available.
Extract data using a selector for one specific user in the room, and subscribe to all changes to the selected data. A Suspense version of this hook is also available.
The reason this hook exists is to enable the most efficient rerendering model for high-frequency updates to other’s presences, which is the following structure:
👉 A Suspense version of this hook is also available, which will never return
null
.
Returns a callback that lets you broadcast custom events to other users in the room.
Listen to custom events sent by other people in the room via
useBroadcastEvent
. Provides the event
along with the connectionId
of
the user that sent the message. If an event was sent from the
Broadcast to a room
REST API, connectionId
will be -1
.
The user
property will indicate which User instance sent the message. This
will typically be equal to one of the others in the room, but it can also be
null
in case this event was broadcasted from the server, using the
Broadcast Event API.
Automatically unsubscribes when the component is unmounted.
Each room contains Storage, a conflict-free data store that multiple users can edit at the same time. When users make edits simultaneously, conflicts are resolved automatically, and each user will see the same state. Storage is ideal for storing permanent document state, such as shapes on a canvas, notes on a whiteboard, or cells in a spreadsheet.
Storage provides three different conflict-free data structures, which you can use to build your application. All structures are permanent and persist when all users have left the room, unlike Presence which is temporary.
LiveObject
- Similar to JavaScript object. Use this for storing records
with fixed key names and where the values don’t necessarily have the same
types. For example, a Person
with a name: string
and an age: number
field. If multiple clients update the same property simultaneously, the last
modification received by the Liveblocks servers is the winner.
LiveList
- An ordered collection of items synchronized across clients.
Even if multiple users add/remove/move elements simultaneously, LiveList will
solve the conflicts to ensure everyone sees the same collection of items.
LiveMap
- Similar to a JavaScript Map. Use this for indexing values that
all have the same structure. For example, to store an index of Person
values
by their name. If multiple users update the same property simultaneously, the
last modification received by the Liveblocks servers is the winner.
To type the Storage values you receive, make sure to set your Storage
type.