Comments - Hooks

The Comments React hooks can be used to fetch, create, and modify threads on the client.

useThreads hook

The most important Comments hook is useThreads, which retrieves a list of each thread in the current room. This can be used to render a list of threads, either using the default components, or primitives. Here’s an example of it used with the default Thread component.

import { useThreads } from "../liveblocks.config";
function Component() { const { threads } = useThreads();
// [{ type: "thread", id: "th_sf8s6sh...", ... }, ...] console.log(threads);
return ( <> {threads.map((thread) => ( <Thread key={thread.id} thread={thread} /> ))} </> );}

There are two versions of the useThreads hook, the Suspense version, which we recommend by default, and the regular version.

useUser hook

The only information Comments stores about users is their user ID, which is set when authenticating with Liveblocks. With the useUser hook, you can fetch a user’s information from their ID. This is particularly helpful when building primitive components, as this allows you fetch their name, avatar, and any other custom data you’ve set.

import { useUser } from "../liveblocks.config";
function Component() { const { user } = useUser("olivier@example.com");
// { name: "Olivier", avatar: "https://...", color: "red" } console.log(user);
return <img src={user.avatar} alt={user.name} />;}

The user data retrieved is set within the resolveUsers function in your liveblocks.config.ts file.

async function resolveUsers({ userIds }) {  // ["olivier@example.com"]  console.log(userIds);
return [ { name: "Olivier", avatar: "https://example.com/olivier.png", color: "red", }, ];}

There are two versions of the useUser hook, Suspense, which we recommend by default, and regular.

Mutation hooks

A number of hooks can be used to mutate comment and thread data, for example:

  • Creating threads
  • Deleting comments
  • Adding emoji reactions

Some of these hooks are quite simple, such as useAddReaction which adds a reaction to a specified comment.

import { CommentData } from "@liveblocks/client";import { useAddReaction } from "../liveblocks.config.ts";
// A button that adds a "⭐" reaction to a commentfunction Component({ comment }: { CommentData }) { const addReaction = useAddReaction();
function handleClick() { addReaction({ threadId: comment.threadId, commentId: comment.commentId, emoji: "⭐", }); }
return <button onClick={handleClick}>Star ⭐</button>;}

Whereas others, such as useCreateThread, have more complex behaviour and are designed to work alongside primitives.

import {  Composer,  ComposerSubmitComment,} from "@liveblocks/react-comments/primitives";import { useCreateThread } from "../liveblocks.config.ts";import { FormEvent } from "react";
// A custom composer that creates a thread on submitfunction MyComposer() { const createThread = useCreateThread();
function handleComposerSubmit( { body }: ComposerSubmitComment, event: FormEvent<HTMLFormElement> ) { event.preventdefault();
const thread = createThread({ body, metadata: {}, }); }
return ( <Composer.Form onComposerSubmit={handleComposerSubmit}> <Composer.Editor /> <Composer.Submit>Create thread</Composer.Submit> </Composer.Form> );}

You can find more information on each mutation hook in our API reference pages:

There’s only one version of these hooks, so it doesn’t make a difference if you export them from suspense in your config file or not.

Hook types

There are two different ways to use the threads and user hooks; with React Suspense, and without it. We recommend using the Suspense versions, as they often result in simpler code.

Suspense hooks

Using Suspense hooks means that any data retrieved, for example threads from getThreads, will never be undefined, and your component will never see an error.

import { useThreads } from "../liveblocks.config.ts";
// Suspense: `threads` is always definedfunction MyThreads() { const { threads } = useThreads();
// [{ type: "thread", id: "th_sf8s6sh...", ... }, ...] console.log(threads);}

To catch errors and display a loading screen, you can use ErrorBoundary and ClientSideSuspense.

import { ClientSideSuspense } from "@liveblocks/react";import { ErrorBoundary } from "react-error-boundary";
// Handle errors and loading state in the component abovefunction Component() { return ( <ErrorBoundary fallback={<div>Error</div>}> <ClientSideSuspense fallback={<div>Loading...</div>}> {() => <MyThreads />} </ClientSideSuspense> </ErrorBoundary> );}

To use Suspense, make sure you’re exporting your hooks from the suspense property in your config file.

liveblocks.config.ts
// ...
export const { suspense: { // Export from here to use Suspense hooks useThreads, useUser, },} = createRoomContext(client, {});

Regular hooks

The regular versions of Liveblocks hooks require you to check for error and isLoading properties. You can then handle these states in the same component.

import { useThreads } from "../liveblocks.config.ts";
// Handle errors and loading state in the same componentfunction MyThreads() { const { threads, error, isLoading } = useThreads();
if (error) { return <div>Error</div>; }
if (isLoading) { return <div>Loading...</div>; }
// Non-Suspense: `threads` is only defined AFTER the `if` checks // [{ type: "thread", id: "th_sf8s6sh...", ... }, ...] console.log(threads);}

To use the regular hooks, make sure you’re exporting from the root level in your config file.

liveblocks.config.ts
// ...
export const { // Export from here to use regular hooks useThreads, useUser,} = createRoomContext(client, {});