> For AI agents: the complete Sanity documentation index is available at [https://www.sanity.io/docs/llms.txt](https://www.sanity.io/docs/llms.txt).

# Use comments in Sanity Studio

Use the comment components in a studio plugin, custom document view, or custom input, with the studio's theme and current user.

A comments panel works in a Sanity Studio plugin, a custom document view, or a custom input, and most of the setup an app needs is already done for you there. This guide covers what a studio changes: the theme, where identity comes from, and deep links.

A studio shares comments with your App SDK apps only when its config turns on Comments API v2 with `beta: {comments: {v2: true}}`. The option is still internal and can change. With it on, the studio reads and writes `sanity.comment` documents through the same collaboration API as this package, so a thread started in your app shows up in the studio, and the reverse. Without it, the studio uses the comments that predate Comments API v2, which are stored per dataset as `comment` documents, and neither side sees the other's threads.

**sanity.config.ts**

```typescript
import {defineConfig} from 'sanity'

export default defineConfig({
  // ...
  beta: {comments: {v2: true}},
})
```

Prerequisites:

- `@sanity/collaboration` installed. See [Get started, Step 1](https://www.sanity.io/docs/collaboration/get-started). The studio already has the Sanity UI peers.
- A studio plugin, custom document view, or custom input to mount the comments panel in.

## What a studio already provides

A studio has Sanity UI's stylesheet and a Sanity UI theme above your component, which is everything the components need. They take the studio's palette and color scheme, so a comments panel in a dark studio is dark. You import no stylesheet and set up no theme.

See [Theme the comment components](https://www.sanity.io/docs/collaboration/theme-the-components).

## Supply identity from the studio

Both `SDKCommentsProvider` and `CommentsProvider` work in a studio. A studio mounts its own App SDK resource so that SDK hooks work anywhere in it, which means `SDKCommentsProvider` resolves identity in a studio the way it does in an app.

Prefer `CommentsProvider` all the same. The studio already has the current user and a user store, and passing them keeps the panel on the identity the studio around it is showing, rather than resolving the same people a second way and without a fallback while it does.

`useCurrentUser` and `useUserStore` from `sanity` supply both. Map each studio user to the `CommentUser` shape, and look users up as the comments ask for them:

**StudioCommentsProvider.tsx**

```tsx
import type {CommentUser} from '@sanity/collaboration'
import {CommentsProvider} from '@sanity/collaboration/ui'
import {type ReactNode, useCallback, useRef, useState} from 'react'
import {useCurrentUser, useUserStore} from 'sanity'

export function StudioCommentsProvider({children}: {children: ReactNode}) {
  const currentUser = useCurrentUser()
  const userStore = useUserStore()
  const [users, setUsers] = useState(new Map<string, CommentUser | null>())
  const requested = useRef(new Set<string>())

  const getUser = useCallback(
    (id: string) => {
      if (users.has(id)) return users.get(id)
      if (!requested.current.has(id)) {
        requested.current.add(id)
        userStore.getUser(id).then((user) =>
          setUsers((prev) =>
            new Map(prev).set(
              id,
              user
                ? {id: user.id, displayName: user.displayName, imageUrl: user.imageUrl}
                : null,
            ),
          ),
        )
      }
      return undefined
    },
    [users, userStore],
  )

  if (!currentUser) return null

  return (
    <CommentsProvider
      currentUser={{
        id: currentUser.id,
        displayName: currentUser.name,
        imageUrl: currentUser.profileImage,
      }}
      getUser={getUser}
    >
      {children}
    </CommentsProvider>
  )
}
```

`getUser` answers `undefined` while a user is being fetched, which renders a skeleton, and `null` once the store says there is no such user. Each answer that arrives replaces the map, so `getUser` gets a new identity and the skeletons re-render into names. Render your panel inside `StudioCommentsProvider`.

A view that takes this route imports from `@sanity/collaboration/ui` and never from `@sanity/collaboration/sdk`, so nothing in the panel depends on the SDK resource.

Either way, the panel inside the provider is identical. Return `null` rather than `undefined` for an ID your store has settled on as absent, or that comment shows a skeleton forever. See [Users and identity](https://www.sanity.io/docs/collaboration/users-and-identity).

`<system>` is the author ID on comments Sanity writes itself. It resolves to "Sanity" with the Sanity logo whatever your lookup returns, which is the same user the studio shows.

## Link with the Sanity Studio router

A shared link opens the panel on a comment the same way in a studio as in an app, with the studio router holding the address. [Link to a comment](https://www.sanity.io/docs/collaboration/link-to-a-comment) covers the props and helpers this uses, and what to do when a link names a comment the panel is hiding.

The Sanity Studio router keeps query parameters on `RouterState._searchParams` as `[key, value]` pairs, which `URLSearchParams` both accepts and spreads back to.

```tsx
import {parseCommentAddress, withCommentAddress} from '@sanity/collaboration'
import {SDKCommentsProvider} from '@sanity/collaboration/sdk'
import {useMemo} from 'react'
import {useRouter, useRouterState} from 'sanity/router'

function Comments() {
  const router = useRouter()
  const searchParams = useRouterState((state) => state._searchParams)

  const params = useMemo(
    () => new URLSearchParams(searchParams ?? []),
    [searchParams],
  )

  return (
    <SDKCommentsProvider
      activeComment={parseCommentAddress(params)}
      onActiveCommentChange={(next) =>
        router.navigate(
          {
            ...router.state,
            _searchParams: [...withCommentAddress(params, next)],
          },
          {replace: true},
        )
      }
    >
      {/* … */}
    </SDKCommentsProvider>
  )
}
```

> [!WARNING]
> Do not reach for sticky parameters. They look convenient here, and they are the wrong tool: a comment ID must not follow the user to the next document.

## Next steps

- Everything else about links to a comment: [Link to a comment](https://www.sanity.io/docs/collaboration/link-to-a-comment).
- Field-level badges next to an input: [Show comment counts](https://www.sanity.io/docs/collaboration/show-comment-counts).

