Collaboration (Beta)

Theme the comment components

Change how the comment components look through the Sanity UI theme, a few typed props, and composition.

The comment components are built on Sanity UI and take their look from its theme. There are three supported ways to change it: the theme, a few typed props, and composition. Try them in that order. Each one later in the list takes more code.

Prerequisites:

Import the Sanity UI stylesheet

Sanity UI v4 ships its static styles as a plain stylesheet, and none of the theming below shows without it. Import it once, wherever your app loads global CSS:

import '@sanity/ui/styles.css'

Change the Sanity UI theme

The comment components ship no theme of their own. They use the Sanity UI theme above them. Wrap your app in ThemeProvider and build a theme:

import {ThemeProvider} from '@sanity/ui'
import {buildTheme} from '@sanity/ui/theme'

// Your own component that renders the comments panel
import {Panel} from './Panel'

const theme = buildTheme()

export function Root() {
  return (
    <ThemeProvider theme={theme}>
      <Panel />
    </ThemeProvider>
  )
}

buildTheme takes a config. Leave a key out and its default stays.

What each theme value changes

Each theme value changes these parts of the components:

Theme value

What it changes

palette.blue

The primary hue: focus ring, selected outline, mention chip, links and buttons

palette.gray

Borders, dividers, muted text, and the tabs

radius

The corners of cards and inputs

space

Insets and gaps

avatar

Avatar sizes, and with them how far the message indents

font

Text

scheme on ThemeProvider

Light or dark

Use a brand palette

Sanity UI names its hues after colors, and the primary tone uses blue. To make the primary hue purple, swap the ramp. The ramps come from @sanity/color. Install it as a dependency of your app to import them:

import * as colors from '@sanity/color'
import {buildTheme} from '@sanity/ui/theme'

const theme = buildTheme({
  palette: {...colors, blue: colors.purple},
})

Swap gray the same way for warmer or cooler neutrals.

Increase corner radius and spacing

radius and space are arrays, indexed by the numbers the components pass to Sanity UI. Larger values give rounder corners and more air:

const theme = buildTheme({
  radius: [0, 3, 6, 10, 14, 18, 24],
  space: [0, 5, 10, 15, 24, 36, 56, 88, 140, 220],
})

Use the dark color scheme

The scheme is a prop on ThemeProvider, not part of the theme:

<ThemeProvider scheme="dark" theme={theme}>
  <Card style={{minHeight: '100vh'}}>
    <Panel />
  </Card>
</ThemeProvider>

Sanity UI paints no background of its own, so if the page behind the panel is not already dark, put the panel in a Card, as above, and give it the height of the page so the dark background reaches the bottom of the window. The browser's default margin on body still shows around the Card as a light frame. Remove it in your global CSS:

body {
  margin: 0;
}

Each of these is a live story in Theming in the reference.

Restyle the inline highlight

The inline comment highlight is a fixed yellow. The palette doesn't change it; only the scheme does, pale on light and deep on dark. To give it your own look, replace the mark with renderHighlight. It receives the comment and its state, (comment, state), and returns a function that the editor calls to wrap the words, (props) => element. The editor calls that function directly rather than mounting it as a component, so it must not use hooks:

import {SDKPortableTextComments} from '@sanity/collaboration/sdk'

// `handle` is the document handle, `threads` what useCommentThreads returns
<SDKPortableTextComments
  {...handle}
  path="body"
  threads={threads}
  renderHighlight={(comment, {selected, onSelect}) =>
    (props) => (
      <mark
        data-comment-id={comment.id}
        className={selected ? 'note note-active' : 'note'}
        onClick={onSelect}
      >
        {props.children}
      </mark>
    )}
/>

renderPendingHighlight does the same for the comment being written. More in Draw your own highlight.

Adjust appearance with props

A few props cover what the theme does not.

actions on Comment and CommentThread. 'hover', the default, reveals the action row on hover and focus. 'always' shows it all the time:

<CommentThread actions="always" thread={thread} />

In a CommentsPanel or SDKCommentsPanel, pass it through threadProps, which both panels take and which takes a function: threadProps={() => ({actions: 'always'})}.

className and style on each component's root, to place it in your layout: width, margin, a minimum width, the root's opacity. They apply to the root element only. Use them for placement, not to reach the elements inside.

Value

Default

How to change it

How far a resolved or retrying comment fades

0.6

style={{opacity: 1}} on Comment or CommentBadge

Where a comment stops shrinking

200px

style={{minWidth: 0}} on Comment or CommentThread

labels rewords the text the components render. See Translate the UI text.

Compose a different layout

When you need a different arrangement rather than a different look, build it from the parts:

Unsupported styling approaches

The comment components don't support these styling approaches:

  • CSS selectors on the components' data-* attributes or class names.
  • Styling elements inside a component, through style, className, or a global stylesheet.

The DOM is internal. The attributes and the structure can change in any release, without notice. If the theme and the props don't reach what you need, compose.

The one exception is identity. data-thread-id on a thread and data-comment-id on a comment or a highlight say which node is which, so you can find one to scroll to it. They are not for styling.

Next steps

Was this page helpful?

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