Installation
Theme
Mode

Collaboration

Comment thread

Where a task gets discussed. Nested replies and @mentions, emoji reactions, file attachments, inline edit and delete, and a composer that autocompletes teammates as you type. Drop it in a Drawer, a detail panel, or a doc.

$npx shadcn@latest add @orbit/comment-thread

Overview

Live — type @ to mention a teammate, hover a comment to reply / react / edit / delete, click an image to preview it full-size, click a reply count to collapse the subtree, or drag files onto the composer.

Comments5
Maya Rivera2h ago
Pushed the empty-state copy — @Sam Chen can you sanity-check the tone before I hand off to eng?
empty-state-before.png
empty-state-revised.png
empty-states.figFIG · 2.3 MB
Sam Chen1h ago
Tone looks great @Maya Rivera — left two line edits in the notes.
tone-notes.pdfPDF · 180 KB
Jon Lake1h ago
Reads well. One nit: the primary CTA should say "Create project", not "New project" — matches the rest of the app.
cta-comparison.png
Priya Kapoor34m ago
Agreed — I’ll fold that in when I pick up the onboarding checklist next sprint.
Sam Chen24m ago
Good catch @Jon Lake — updated. @Priya Kapoor this unblocks the onboarding checklist, want to take it?
Enter to send · Shift+Enter for a new line

Capabilities

Each comment is a row of identity, body and reactions, with quiet actions that appear on hover. Nothing leaves the thread.

@ mentions

Type @ in the composer to autocomplete teammates; posted mentions render as tappable chips that notify the person.

Reactions

A quick emoji set — react with a click, see who reacted, and toggle yours. Counts roll up per emoji.

Inline edit & delete

Your own comments show edit and delete on hover; editing happens in place with an “edited” marker, no page reload.

Composer

A growing field with Enter-to-send and Shift+Enter for new lines, anchored to the signed-in avatar.

Nested replies

Reply to any comment and the response nests beneath it with a guide line; delete a parent and its whole subtree goes with it.

File attachments

Attach files from the composer or any reply — images preview as thumbnails, everything else as type-colored cards, removable before you post.

Image lightbox

Click any image thumbnail to open it full-size over a scrim; ← → step through the rest of that comment's images and Escape closes.

Tune it to the surface

Tighten the spacing for dense panels, drop the composer for a read-only history, or switch threading and attachments off for a flat, text-only log. Each instance is live.

Compact — density="compact"
Comments5
Maya Rivera2h ago
Pushed the empty-state copy — @Sam Chen can you sanity-check the tone before I hand off to eng?
empty-state-before.png
empty-state-revised.png
empty-states.figFIG · 2.3 MB
Sam Chen1h ago
Tone looks great @Maya Rivera — left two line edits in the notes.
tone-notes.pdfPDF · 180 KB
Jon Lake1h ago
Reads well. One nit: the primary CTA should say "Create project", not "New project" — matches the rest of the app.
cta-comparison.png
Priya Kapoor34m ago
Agreed — I’ll fold that in when I pick up the onboarding checklist next sprint.
Sam Chen24m ago
Good catch @Jon Lake — updated. @Priya Kapoor this unblocks the onboarding checklist, want to take it?
Enter to send · Shift+Enter for a new line
Read-only — showComposer={false}
Comments5
Maya Rivera2h ago
Pushed the empty-state copy — @Sam Chen can you sanity-check the tone before I hand off to eng?
empty-state-before.png
empty-state-revised.png
empty-states.figFIG · 2.3 MB
Sam Chen1h ago
Tone looks great @Maya Rivera — left two line edits in the notes.
tone-notes.pdfPDF · 180 KB
Jon Lake1h ago
Reads well. One nit: the primary CTA should say "Create project", not "New project" — matches the rest of the app.
cta-comparison.png
Priya Kapoor34m ago
Agreed — I’ll fold that in when I pick up the onboarding checklist next sprint.
Sam Chen24m ago
Good catch @Jon Lake — updated. @Priya Kapoor this unblocks the onboarding checklist, want to take it?
Flat & plain — threaded={false} · allowAttachments={false}
Comments5
Maya Rivera2h ago
Pushed the empty-state copy — @Sam Chen can you sanity-check the tone before I hand off to eng?
empty-state-before.png
empty-state-revised.png
empty-states.figFIG · 2.3 MB
Sam Chen1h ago
Tone looks great @Maya Rivera — left two line edits in the notes.
tone-notes.pdfPDF · 180 KB
Jon Lake1h ago
Reads well. One nit: the primary CTA should say "Create project", not "New project" — matches the rest of the app.
cta-comparison.png
Priya Kapoor34m ago
Agreed — I’ll fold that in when I pick up the onboarding checklist next sprint.
Sam Chen24m ago
Good catch @Jon Lake — updated. @Priya Kapoor this unblocks the onboarding checklist, want to take it?
Enter to send · Shift+Enter for a new line

Props

PropTypeDefaultDescription
commentsCommentData[]Controlled comment list — flat array; replies link to their parent via parentId.
defaultCommentsCommentData[]Initial list when uncontrolled.
onCommentsChange(comments: CommentData[]) => voidFires with the full next list after any add, edit, delete or reaction.
peopleCommentPerson[][]Everyone in the thread — feeds avatars, the @mention menu, and mention highlighting. Optional color per person (tokens/color-mix).
currentUserstringId of the signed-in person (from people). Owns the composer and gates edit/delete to their own comments.
density"comfortable" | "compact""comfortable"Compact tightens row padding and the composer height for dense panels.
showComposerbooleantrueHide for a read-only history — reply actions hide with it.
threadedbooleantrueNested replies with guide lines and collapsible subtrees. false renders a flat log with no reply action.
allowAttachmentsbooleantruePaperclip picker + drag-and-drop dropzone in the composer and replies.
emojisstring[]["👍","❤️","🎉","✅","👀","🚀"]The reaction picker's emoji set.
nowstring | number | DateClock override for relative timestamps — pass a fixed value so demos and tests render stable times.
headingstring"Comments"Header title next to the count pill.
onAdd / onReply / onEdit / onDelete / onReactfunctionGranular events, fired alongside onCommentsChange — e.g. onReact(id, emoji, reacted).
…restHTMLAttributes<HTMLDivElement>Everything else is forwarded to the root element, including ref.

Usage

Do

Attach it to one record

A thread belongs to a single task, doc or record. Keep the scope tight so notifications and @mentions stay meaningful.

Don’t

Use it as a chat room

For real-time team chat use a dedicated channel. Comments are durable, record-scoped discussion — not a firehose.

Do

Make mentions actionable

An @mention should notify and link to the person. Show their name and avatar so the reference is unambiguous.

Don’t

Hide edit history entirely

When a comment is edited, mark it. Silent edits erode trust in a thread people rely on for decisions.

Keyboard & accessibility

The mention menu is a listbox the composer controls via aria-activedescendant — focus never leaves the textarea. Image thumbnails are focusable buttons that open a labelled aria-modal dialog; focus moves into the lightbox and returns to the thumbnail on close. Hover actions also reveal on keyboard focus, reaction chips expose who reacted and aria-pressed, and Escape closes any open overlay. The 3px focus ring uses the themed --ring token.

@Open the mention autocomplete
↑ ↓Move the mention highlight
EnterPost the comment / pick a mention
Shift + EnterNew line in the composer
⌘ / Ctrl + EnterSave an inline edit
Enter / SpaceOpen the focused image thumbnail in the lightbox
← →Step through images in the lightbox
EscClose mention menu / picker / image preview, cancel edit or reply