Collaboration (Beta)

Design principles of the comment components

Six promises the comment components make, and what each one means for the code you write.

The comment components in @sanity/collaboration/ui make six promises. Each one is also a prediction about what you will and will not have to fight.

You own the data

Components take the threads, the thread, the comment, or the count as props. Not one of the components in @sanity/collaboration/ui calls the SDK, subscribes to anything, or fetches on mount.

Reads and writes are a page of your own code, wiring the SDK hooks to the components. In exchange, the same components render comments from a GROQ query, from a mentions inbox, or from a fixture in a test, and the data layer can change under them without any of them noticing.

One provider sets the environment

Everything identical for every comment on a surface is set once, on CommentsProvider: the current user, the user lookup, the mention options, the reaction set, time formatting, and the capability policy.

Everything that differs per comment is a prop on the component that owns it.

This diagram could not be rendered.

              flowchart LR
  env["currentUser · getUser · mentionOptions · reactionOptions · formatTime · capabilities"] --> provider["CommentsProvider"]
  provider -->|"context, inherited"| comment["Comment components"]
  data["comment · thread · threads · count"] -->|"props"| comment
  callbacks["onEdit · onDelete · onReply · onStatusChange · onReactionToggle · onCopyLink · onRetry"] -->|"props"| comment
            

A subtree that needs different values nests another CommentsProvider. There is no second configuration system.

The selected comment is part of that environment: the provider keeps it, so a toolbar, an editor, and a panel under one provider agree without passing it between them. Two panels that should not share a selection get a provider each. See Comment navigation and deep links.

A callback is a control

An action renders only when its callback is present, and is disabled when the matching capability resolves to false. So there is no showDeleteButton prop: whether an action exists is the same question as whether you passed a handler for it, and passing none is what makes a read-only rendering.

See Comment components.

Defaults first, primitives when they stop fitting

Every primitive the default comment is assembled from is exported, so when the default composition does not fit you drop one level rather than reaching for a flag. Dropping a level keeps the skeletons, the accessible names, the reaction grouping, and the action list. Only the arrangement becomes yours.

The same holds underneath the components: the SDK hooks drop to SDK actions, the Sanity client, and the HTTP API, one rung at a time.

See Comment components and Levels of abstraction in the comment stack.

Your router owns the URL

You hand CommentsProvider the active comment address and write back what it reports. The components do the rest: matching the address, holding it pending while data loads, expanding collapsed replies, scrolling, focus, and selecting the thread.

This works with React Router, Next.js, TanStack Router, and no router at all, because what passes between you is a value and a callback rather than an integration. With no router, leave activeComment and onActiveCommentChange off and the provider keeps the address in state. Nothing here reads window.location. See Comment navigation and deep links.

Theme without forking

The components are built on Sanity UI. To change how they look, theme them: the Sanity UI theme first, then a few typed props, then composition. The DOM is not part of the API, so a stylesheet aimed at it can break in any release.

What you cannot override is identity and behavior: the identity attributes, the ref, and the selection handlers. They are applied after your props, because the other five promises are built on them. Names can be reworded with labels, and a component's own name replaced with aria-label.

Was this page helpful?

Sanity logosanity.ioAll Systems Operational© Sanity 2026
Change site theme