API Reference - Comments

@liveblocks/react-comments provides you with React components to build a commenting experience. Read our getting started guide to learn more.

Components

Customization

Styling

The default components come with default styles, import them either at the root of your app or directly in CSS depending on your setup.

layout.tsx
import "@liveblocks/react-comments/styles.css";
globals.css
@import "@liveblocks/react-comments/styles.css";

They are built around a few customizable CSS variables:

/* Basic */--lb-radius: 0.5em;--lb-spacing: 1em;--lb-accent: #17f;--lb-accent-foreground: #fff;--lb-background: #fff;--lb-foreground: #111;
/* Advanced */--lb-icon-size: 20px;--lb-icon-weight: 1.5px;--lb-avatar-radius: 50%;--lb-button-radius: calc(0.75 * var(--lb-radius));--lb-transition-duration: 0.1s;--lb-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);--lb-elevation-shadow: 0 0 0 1px rgb(0 0 0 / 4%), 0 2px 6px rgb(0 0 0 / 8%), 0 8px 26px rgb(0 0 0 / 12%);--lb-tooltip-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 4px 12px rgb(0 0 0 / 12%);--lb-elevation-background: var(--lb-background);--lb-elevation-foreground: var(--lb-foreground);--lb-tooltip-background: #222;--lb-tooltip-foreground: #fff;--lb-accent-contrast: 8%;--lb-foreground-contrast: 6%;--lb-elevation-foreground-contrast: 6%;--lb-tooltip-foreground-contrast: 10%;

If these CSS variables aren’t enough, every elements within the components have clearly defined classes. Additionaly, some elements also have data attributes to provide contextual information (e.g. data-variant="primary" on .lb-button, data-self on .lb-comment-mention, data-loading on .lb-avatar and .lb-user, etc.).

Dark mode

Dark mode can be implemented by customizing some of the default CSS variables, and we provide styles that do so out-of-the-box. They exist in two versions:

  • media-query uses the prefers-color-scheme media query.
  • attributes targets either the dark class, the data-theme attribute if it’s set to "dark", or the data-dark attribute.
layout.tsx
import "@liveblocks/react-comments/styles.css";
// Choose one depending on your setupimport "@liveblocks/react-comments/styles/dark/media-query.css";import "@liveblocks/react-comments/styles/dark/attributes.css";
globals.css
@import "@liveblocks/react-comments/styles.css";
/* Choose one depending on your setup */@import "@liveblocks/react-comments/styles/dark/media-query.css";@import "@liveblocks/react-comments/styles/dark/attributes.css";
Browser support

The default styles use modern CSS features like container queries, :has() and color-mix(). While container queries and :has() are used as progressive enhancements and aren’t required, color-mix() is used extensively to generate the various color scales. If you need to support browser versions that don’t support color-mix(), you can specify the color scales manually:

--lb-accent-subtle: ;--lb-accent-moderate: ;--lb-accent-tertiary: ;--lb-accent-secondary: ;--lb-foreground-subtle: ;--lb-foreground-moderate: ;--lb-foreground-tertiary: ;--lb-foreground-secondary: ;--lb-elevation-foreground-subtle: ;--lb-elevation-foreground-moderate: ;--lb-elevation-foreground-tertiary: ;--lb-elevation-foreground-secondary: ;--lb-tooltip-foreground-subtle: ;--lb-tooltip-foreground-moderate: ;--lb-tooltip-foreground-tertiary: ;--lb-tooltip-foreground-secondary: ;

Overrides

Overrides can be used to customize the components’ strings and localization-related properties like the locale and reading direction.

They can be set globally once for all components using CommentsConfig:

import { CommentsConfig } from "@liveblocks/react-comments";
export function App() { return ( <CommentsConfig overrides={{ locale: "fr", UNKNOWN_USER: "Anonyme" /* ... */ }} > {/* ... */} </CommentsConfig> );}

Or they can be set per-component (which will take precedence over the global ones) for contextual cases like a <Composer /> used specifically to reply for example:

<Composer  overrides={{    COMPOSER_PLACEHOLDER: "Reply to thread…",    COMPOSER_SEND: "Reply",  }}/>

Thread

Displays a thread of comments, with a composer to reply to it.

<>  {threads.map((thread) => (    <Thread key={thread.id} thread={thread} />  ))}</>
PropTypeDescription
threadThreadData<{ resolved?: boolean }>The thread to display.
showComposerboolean | "collapsed" | undefinedHow to show or hide the composer to reply to the thread.
Defaults to "collapsed".
showActionsboolean | "hover" | undefinedHow to show or hide the actions.
Defaults to "hover".
showResolveActionboolean | undefinedWhether to show the action to resolve the thread.
Defaults to true.
indentCommentBodyboolean | undefinedWhether to indent the comments’ bodies.
Defaults to true.
showDeletedCommentsboolean | undefinedWhether to show deleted comments.
Defaults to false.
onResolveChange(resolved: boolean) => voidThe event handler called when changing the resolved status.
onCommentEdit(comment: CommentData) => voidThe event handler called when a comment is edited.
onCommentDelete(comment: CommentData) => voidThe event handler called when a comment is deleted.
onAuthorClick(userId: string, event: MouseEvent<HTMLElement>) => void | undefinedThe event handler called when clicking on a comment’s author.
onMentionClick(userId: string, event: MouseEvent<HTMLElement>) => void | undefinedThe event handler called when clicking on a mention.
overridesPartial<ThreadOverrides & CommentOverrides & ComposerOverrides> | undefinedOverride the component’s strings.

Comment

Displays a single comment.

<>  {thread.comments.map((comment) => (    <Comment key={comment.id} comment={comment} />  ))}</>
PropTypeDescription
commentCommentDataThe thread to display.
showActionsboolean | "hover" | undefinedHow to show or hide the actions.
Defaults to "hover".
indentBodyboolean | undefinedWhether to indent the comment’s body.
Defaults to true.
showDeletedboolean | undefinedWhether to show the comment if it was deleted. If set to false, it will render deleted comments as null.
Defaults to false.
onEdit(comment: CommentData) => voidThe event handler called when the comment is edited.
onDelete(comment: CommentData) => voidThe event handler called when the comment is deleted.
onAuthorClick(userId: string, event: MouseEvent<HTMLElement>) => void | undefinedThe event handler called when clicking on the author.
onMentionClick(userId: string, event: MouseEvent<HTMLElement>) => void | undefinedThe event handler called when clicking on a mention.
overridesPartial<CommentOverrides & ComposerOverrides> | undefinedOverride the component’s strings.

Composer

Displays a composer to create comments.

<Composer />

By default, submitting the composer will create a new thread. If you provide a threadId prop, it will reply to that thread, and if you also provide a commentId prop, it will edit that comment. If you want to customize this, you can use event.preventDefault() in onComposerSubmit to disable the default behavior and call the mutation methods (e.g. createThread) manually.

<Composer  onComposerSubmit={({ body }, event) => {    event.preventDefault();
createThread({ body, metadata: { x: 80, y: 120, }, }); }}/>
PropTypeDescription
threadIdstring | undefinedThe ID of the thread to reply to or to edit a comment in.
commentIdstring | undefinedThe ID of the comment to edit.
onComposerSubmit((comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => Promise<void> | void) | undefinedThe event handler called when the composer is submitted.
defaultValueCommentBody | undefinedThe composer’s initial value.
collapsedboolean | undefinedWhether the composer is collapsed. Setting a value will make the composer controlled.
onCollapsedChange(collapsed: boolean) => voidThe event handler called when the collapsed state of the composer changes.
defaultCollapsedboolean | undefinedWhether the composer is initially collapsed. Setting a value will make the composer uncontrolled.
disabledboolean | undefinedWhether the composer is disabled.
autoFocusboolean | undefinedWhether to focus the composer on mount.
overridesPartial<ComposerOverrides> | undefinedOverride the component’s strings.

Primitives

Primitives are headless/unstyled components that can be used to create custom experiences with full control.

Composition

All primitives are composable: they forward their props and refs, merge their classes and styles, and chain their event handlers.

Inspired by Radix (and powered by its Slot utility), most of the primitives also support an asChild prop to replace the rendered element by any provided child, and both set of props will be merged. Learn more about this concept on Radix’s composition guide.

import { Button } from "@/my-design-system";
// A default <button> element<Composer.Submit disabled>Send</Composer.Submit>;
// An existing custom <Button> component<Composer.Submit disabled asChild> <Button variant="primary">Send</Button></Composer.Submit>;

Comment

Anatomy
import { Comment } from "@liveblocks/react-comments/primitives";
() => ( <Comment.Body renderMention={() => <Comment.Mention />} renderLink={() => <Comment.Link />} />);

Comment.Body

Displays a comment body.

<Comment.Body body={comment.body} />
PropTypeDescription
bodyCommentBody | undefinedThe comment body to display. If not defined, the component will render null.
renderMentionComponentType<CommentRenderMentionProps> | undefinedThe component used to render mentions.
Defaults to the mention’s userId prefixed by an @.
renderLinkComponentType<CommentRenderLinkProps> | undefinedThe component used to render links.
Defaults to the link’s children property.
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.
renderMention

A render prop that accepts a component used to render mentions.

<Comment.Body  renderMention={({ userId }) => <Comment.Mention>@{userId}</Comment.Mention>}/>
PropTypeDescription
userIdstringThe mention’s user ID.
renderLink

A render prop that accepts a component used to render links.

<Comment.Body  renderLink={({ href, children }) => (    <Comment.Link href={href}>{children}</Comment.Link>  )}/>
PropTypeDescription
hrefstringThe link’s absolute URL.
childrenReactNodeThe link’s content.

Comment.Mention

Displays mentions within Comment.Body’s renderMention prop.

<Comment.Mention>@{userId}</Comment.Mention>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Comment.Link

Displays links within Comment.Body’s renderLink prop.

<Comment.Link href={href}>{children}</Comment.Link>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Composer

Anatomy
import { Composer } from "@liveblocks/react-comments/primitives";
() => ( <Composer.Form> <Composer.Editor renderMention={() => <Composer.Mention />} renderMentionSuggestions={() => ( <Composer.Suggestions> <Composer.SuggestionsList> <Composer.SuggestionsListItem /> </Composer.SuggestionsList> </Composer.Suggestions> )} renderLink={() => <Composer.Link />} /> <Composer.Submit /> </Composer.Form>);

Composer.Form

Surrounds the composer’s content and handles submissions.

<Composer.Form onComposerSubmit={({ body }) => {}}>{/* ... */}</Composer.Form>
PropTypeDescription
onComposerSubmit((comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => Promise<void> | void) | undefinedThe event handler called when the form is submitted.
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Composer.Editor

Displays the composer’s editor.

<Composer.Editor placeholder="Write a comment…" />
PropTypeDescription
defaultValueCommentBody | undefinedThe editor’s initial value.
placeholderstring | undefinedThe text to display when the editor is empty.
disabledboolean | undefinedWhether the editor is disabled.
autoFocusboolean | undefinedWhether to focus the editor on mount.
dir"ltr" | "rtl" | undefinedThe reading direction of the editor and related elements.
renderMentionComponentType<ComposerRenderMentionProps> | undefinedThe component used to render mentions.
Defaults to the mention’s userId prefixed by an @.
renderMentionSuggestionsComponentType<ComposerRenderMentionSuggestionsProps> | undefinedThe component used to render mention suggestions.
Defaults to a list of the suggestions’ userId.
renderLinkComponentType<ComposerRenderLinkProps> | undefinedThe component used to render links.
Defaults to the link’s children property.
AttributeValue
data-focusedPresent when the component is focused.
data-disabledPresent when the component is disabled.
renderMention

A render prop that accepts a component used to render mentions.

<Composer.Editor  renderMention={({ userId, isSelected }) => (    <Composer.Mention>@{userId}</Composer.Mention>  )}/>
PropTypeDescription
userIdstringThe mention’s user ID.
isSelectedbooleanWhether the mention is selected.
renderMentionSuggestions

A render prop that accepts a component used to render mention suggestions.

PropTypeDescription
userIdsstring[]The list of suggested user IDs.
selectedUserIdstring | undefinedThe currently selected user ID.
<Composer.Editor  renderMentionSuggestions={() => (    <Composer.Suggestions>      <Composer.SuggestionsList>        <Composer.SuggestionsListItem />      </Composer.SuggestionsList>    </Composer.Suggestions>  )}/>
renderLink

A render prop that accepts a component used to render links.

<Composer.Editor  renderLink={({ href, children }) => <Composer.Link>{children}</Composer.Link>}/>
PropTypeDescription
hrefstringThe link’s absolute URL.
childrenReactNodeThe link’s content.

Composer.Mention

Displays mentions within Composer.Editor’s renderMention prop.

<Composer.Mention>@{userId}</Composer.Mention>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.
AttributeValue
data-selectedPresent when the mention is selected.

Composer.Suggestions

Surrounds a list of suggestions within Composer.Editor.

<Composer.Suggestions>{/* … */}<Composer.Suggestions>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Composer.SuggestionsList

Displays a list of suggestions within Composer.Editor.

<Composer.SuggestionsList>{/* … */}</Composer.SuggestionsList>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Composer.SuggestionsListItem

Displays a suggestion within Composer.SuggestionsList.

<Composer.SuggestionsList>  {userIds.map((userId) => (    <Composer.SuggestionsListItem key={userId} value={userId}>      @{userId}    </Composer.SuggestionsListItem>  ))}</Composer.SuggestionsList>
PropTypeDescription
valuestringThe suggestion’s value.
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.
AttributeValue
data-selectedPresent when the item is selected.

Composer.Link

Displays links within Composer.Editor’s renderLink prop.

<Composer.Link href={href}>{children}</Composer.Link>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Composer.Submit

A button to submit the composer.

<Composer.Submit>Send</Composer.Submit>
PropTypeDescription
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Timestamp

Displays a formatted date, and automatically re-renders to support relative formatting. Defaults to relative formatting for recent dates and a short absolute formatting for older ones.

<Timestamp date={new Date()} />
PropTypeDescription
dateDate | string | numberThe date to display.
children((date: Date) => ReactNode) | undefinedA function to format the displayed date.
Defaults to a relative date formatting function.
titlestring | ((date: Date) => string) | undefinedThe title attribute’s value or a function to format it.
Defaults to an absolute date formatting function.
intervalnumber | false | undefinedThe interval in milliseconds at which the component will re-render. Can be set to false to disable re-rendering.
Defaults to 30000.
asChildboolean | undefinedReplace the rendered element by the one passed as a child.
Defaults to false.

Hooks

useComposer

Returns states and methods related to the composer. Can only be used within Composer.Form.

import { useComposer } from "@liveblocks/react-comments/primitives";
const { isEmpty, submit } = useComposer();