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-threadOverview
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.
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.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| comments | CommentData[] | — | Controlled comment list — flat array; replies link to their parent via parentId. |
| defaultComments | CommentData[] | — | Initial list when uncontrolled. |
| onCommentsChange | (comments: CommentData[]) => void | — | Fires with the full next list after any add, edit, delete or reaction. |
| people | CommentPerson[] | [] | Everyone in the thread — feeds avatars, the @mention menu, and mention highlighting. Optional color per person (tokens/color-mix). |
| currentUser | string | — | Id 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. |
| showComposer | boolean | true | Hide for a read-only history — reply actions hide with it. |
| threaded | boolean | true | Nested replies with guide lines and collapsible subtrees. false renders a flat log with no reply action. |
| allowAttachments | boolean | true | Paperclip picker + drag-and-drop dropzone in the composer and replies. |
| emojis | string[] | ["👍","❤️","🎉","✅","👀","🚀"] | The reaction picker's emoji set. |
| now | string | number | Date | — | Clock override for relative timestamps — pass a fixed value so demos and tests render stable times. |
| heading | string | "Comments" | Header title next to the count pill. |
| onAdd / onReply / onEdit / onDelete / onReact | function | — | Granular events, fired alongside onCommentsChange — e.g. onReact(id, emoji, reacted). |
| …rest | HTMLAttributes<HTMLDivElement> | — | Everything else is forwarded to the root element, including ref. |
Usage
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.
Use it as a chat room
For real-time team chat use a dedicated channel. Comments are durable, record-scoped discussion — not a firehose.
Make mentions actionable
An @mention should notify and link to the person. Show their name and avatar so the reference is unambiguous.
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.