Comment thread

Threaded comments with replies, reactions, mentions, inline edit, and resolve.

pnpm dlx shadcn@latest add @uiarc/comment-thread
Live · keyboard ready

Hero headline

4 comments
  • Jasmine Brooks2h

    Can we try a shorter headline here? @Emma Collins had a version that fit on one line at 390 px.

    • Emma Collins1h

      Yes, “Plan less. Ship more.” Dropping it into the file now.

    • Marcus Johnson48m

      Works for me. It also saves the second line on mobile.

  • Daniel Kim30m

    Once this lands I'll update the social image copy to match. @Chloe Nguyen can you check the click through numbers after?

Signed in as Emma Collins

  • Review threads on documents, designs, tickets, or pull requests.
  • Discussions that need replies, reactions, mentions, and a resolved state.
  • Sidebars of feedback attached to one object.
  • Use chat-thread for real-time back-and-forth conversation.
  • Use ai-chat for assistant conversations with streaming replies.
  • Use mention-input alone when you only need a composer.

Installation

Add Comment thread with the shadcn CLI, or copy the source by hand.

pnpm dlx shadcn@latest add @uiarc/comment-thread

Adds the component and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:

example.tsx
import { CommentThread, type ThreadComment } from "@/registry/components/comment-thread/comment-thread"; export function HeadlineDiscussion({ initial, me }: { initial: ThreadComment[]; me: { id: string; name: string; avatar?: string } }) {  return (    <CommentThread      title="Hero headline"      currentUser={me}      defaultComments={initial}      onCommentsChange={(_, event) => syncComment(event)}      onResolvedChange={resolved => setThreadResolved("hero", resolved)}    />  );}

API reference

2 parts. The first is the root.

CommentThread

A threaded discussion: nested replies that collapse, reactions with rolling counts, in-place edit and delete, @mention autocomplete, and resolving that folds the thread into a chip.

PropTypeDefaultDescription
commentsThreadComment[]–The comment tree (controlled).
defaultCommentsThreadComment[][]Initial tree when uncontrolled.
onCommentsChange(comments: ThreadComment[], event: CommentThreadEvent) => void–Called with the full next tree and what changed: reply, edit, delete, or react.
currentUserRequiredCommentAuthor–The person writing. Their comments can be edited and deleted.
peopleCommentAuthor[]–People who can be mentioned. Defaults to everyone in the thread.
resolvedboolean–Resolved state (controlled).
defaultResolvedbooleanfalseInitial resolved state.
onResolvedChange(resolved: boolean) => void–Called on Resolve and Reopen.
titleReactNode–What the thread is about, shown in the header, such as "Hero headline".
reactionsstring[]["👍", "❤️", "🎉", "👀", "🚀", "✅"]Emoji offered by the reaction picker.
placeholderstring–Composer placeholder.
maxDepthnumber2Replies deeper than this attach to the deepest allowed parent.
nowLabelstring–createdAt label for comments written now.
classNamestring–Extra class on the section.
refRef<HTMLElement>–The section element.

ThreadComment

One comment in the tree.

PropTypeDefaultDescription
idRequiredstring–Stable id.
authorRequired{ id: string; name: string; avatar?: string }–Author. Initials show without an avatar.
bodyRequiredstring–Plain text. "@Full Name" mentions of known people are highlighted.
createdAtRequiredstring–Display label such as "2h" or "Sep 18".
editedboolean–Shows an edited mark.
deletedboolean–A deleted comment with replies stays as a quiet placeholder.
reactions{ emoji: string; users: string[] }[]–Reactions and who gave them.
repliesThreadComment[]–Nested replies.
⌘+EnterorCtrl+Enter
Sends the composer.
@
Opens mention suggestions in the composer.
ArrowUporArrowDown
Move through mention suggestions.
EnterorTab
Insert the active mention.
Escape
Closes suggestions, then cancels an edit or reply; also closes the reaction picker or delete confirmation.
  • The composer is a combobox with a listbox of people and aria-activedescendant.
  • Each comment is an article labelled with author and time; deleted ones read "Deleted comment".
  • Reaction chips are toggle buttons with aria-pressed and a label like "👍 3, including you".
  • Reply toggles expose aria-expanded; icon-only actions have specific labels.
  • The thread height springs when replies expand, comments arrive, or the thread folds into the resolved chip.
  • Reaction counts roll to the new value; chips move with layout animation as they are added or removed.
  • Edit, confirm, and picker rows slide in from the side.
  • Reduced motion swaps content without travel and turns off transitions.
  • Below 420px the composer hides its avatar and nested replies indent less (12px).
  • Hover styles only apply on fine pointers; every action is a visible button on touch.
  • Each change rebuilds the tree immutably and renders every comment; paginate or collapse very long threads.
  • Reply nesting is capped by maxDepth, which keeps indentation and render depth bounded.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Choose it for comments attached to a document, design, or record. Persist changes with the event passed to onCommentsChange rather than diffing trees.
  • currentUser decides which comments show Edit and Delete; enforce the same rule on your server.
  • Deleting a comment with replies keeps a placeholder so the thread structure survives.

The full library index for assistants is at /llms.txt.