# Document-level comments, multi-document panels and DOM styling props

**Version:** v0.2.0

**Published:** October 29, 2026

This release lets a comment belong to a whole document, groups comments from several documents in one list or panel, and replaces Sanity UI layout props with plain DOM attributes. It also renames the SDK components after their `@sanity/collaboration/ui` counterparts. Several of these changes break `CommentsPanel` and styling code written for `0.1.0`.

## New features

- Document-level comments: a thread can be on a document as a whole, not only on one of its fields, with `fieldPath: ''`. `compose={{fieldPath: ''}}` starts one from `CommentsPanel`, whose composer then prompts "Comment on the document…". `CommentList` heads these threads with the document when it names documents, and otherwise with the new `labels.list.document` (default "Document"). The Sanity Studio comments inspector doesn't list document-level comments. Document-level comments need `@sanity/sdk` and `@sanity/sdk-react` 3.11.0 or later.
- Multi-document lists: `CommentList` and `CommentsPanel` group comments from several documents. Groups are one field of one document, keyed on the new optional `CommentData.documentId` (and `documentType`). Once the threads span more than one document, each heading names the document as well as the field. `documentTitle(group)` names a group's document, `renderFieldHeading` and `renderFieldAction` receive the group's `documentId`, and `scrollToField` takes a `CommentTarget` as well as a field path.
- Always-visible actions: `Comment` and `CommentThread` take `actions: 'hover' | 'always'`. `'always'` keeps the action row in view instead of revealing it on hover or focus. The default is `'hover'`.
- `SDKCommentsPanel` takes `onCommentCreated(comment)`, called once a new thread is created, as on `SDKPortableTextComments`. Use it to close a composer opened with `compose` or to select the new thread.
- `useSDKThreadCallbacks` also accepts a function from a thread to its document's handle.

## Improvements

- In development, `CommentsPanel` warns when the threads span several documents and `compose` has no `documentId`.
- `panel.composerPlaceholder` receives the full target, so you can name the document as well as the field.
- Multi-document headings truncate the document title before the field, and show the full heading in a tooltip when needed.
- The inline comment button opens from the bottom-right corner of the selection and runs right, or left when it doesn't fit, instead of centered under the selection. The `placement` prop on `InlineCommentInput` still overrides it.

## Breaking changes

- SDK component names: `DocumentCommentsPanel` is now `SDKCommentsPanel` (with `SDKCommentsPanelProps` and `SDKCommentsPanelAction`), and `InlineCommentsPlugin` is now `SDKPortableTextComments` (with `SDKPortableTextCommentsProps`). Props and behavior are unchanged, so update the imports.
- Styling props: `Comment`, `CommentThread`, `CommentList` and `CommentBadge` no longer accept Sanity UI props (`padding`, `radius`, `tone`, `border`, `shadow`, `gap`, `mode`, `fontSize` and others). Each takes the DOM attributes of the element it renders: `className`, `style`, `id`, `aria-*` and event handlers. Style them through the Sanity UI theme, and use `className` or `style` for placement. The `data-*` attributes in the rendered DOM aren't a styling API and can change in any release. See [Theme the components](https://www.sanity.io/docs/collaboration/theme-the-components).
- `onThreadCreate(fieldPath, message)` is now `onThreadCreate(target, message)`, and `labels.composerPlaceholder(fieldPath)` is now `composerPlaceholder(target)`. `target` is the new `CommentTarget`, `{fieldPath, documentId?}`, exported from the root entry.
- `draftKey` is removed. Drafts are always kept per target, so they survive a composer unmounting and carry between panels over the same document. The new optional `draftScope` string prefixes their keys, to keep two panels over one document apart.
- The `fieldPath` prop on `CommentsPanel` is replaced by `compose: {fieldPath, documentId?, onDiscard?}`. `documentId` says which document the composer writes to, and `onDiscard` makes it a composer opened on request: it takes focus when it mounts, and Cancel or Escape calls `onDiscard`. `SDKCommentsPanel` takes `compose` without `documentId`, since its document is the handle's.
- `renderFieldHeading(group)` is now `renderFieldHeading(group, {heading})` on both `CommentList` and `CommentsPanel`, where `heading` is the built-in heading. Both components now read the result the same way: `null` renders no heading and `undefined` renders the built-in one.
- The composer above the list remounts when its target changes, so unsent text stays with the field and document it was written for.

