The composer supports @ user mentions, an emoji picker, and a rich-formatting toggle (a plain textarea by default, switching to the markdown editor on demand — Teams-style). Comments are stored and rendered as markdown regardless of which mode authored them, so mentions and emoji round-trip everywhere.
Installation
pnpm dlx @docyrus/cli add @docyrus/ui-comments-panelnpx shadcn@latest add avatar button dropdown-menu input popover scroll-area separator skeletonpnpm add react-markdown remark-gfm date-fnsComposes the Emoji Picker and Simple Markdown Editor components — the CLI installs them automatically as registry dependencies.
Usage
import { CommentsPanel } from "@docyrus/ui/components/comments-panel";
import type { CommentsPanelProps } from "@docyrus/ui/components/comments-panel";
const comments: CommentsPanelProps['comments'] = [
{ id: "c-1", message: "Looks good to me.", attachments: null, created_on: "2025-12-17T10:00:00Z", last_modified_on: "2025-12-17T10:00:00Z", created_by: "u-alice", record_id: "rec-1", parent_id: null }
];
function Demo() {
return (
<CommentsPanel
comments={comments}
currentUser={{ id: "u-alice", firstname: "Alice", lastname: "Johnson" }}
users={[
{ id: "u-alice", firstname: "Alice", lastname: "Johnson" },
{ id: "u-bob", firstname: "Bob", lastname: "Smith" }
]}
onCreateComment={({ message, mentions }) => console.log(message, mentions)}
onUpdateComment={(id, message, mentions) => console.log(id, message, mentions)}
onDeleteComment={(id) => console.log("delete", id)}
/>
);
}Mentions, emoji & rich formatting
@mentions — typing@opens a user picker sourced from theusersprop. The selected user is stored inline as a markdown link ([@Name](mention://<userId>)) and rendered as a highlighted chip.onCreateComment/onUpdateCommentreceive the referenced user ids inmentions. Disable withenableMentions={false}.- Emoji — the composer's emoji button inserts native unicode at the caret in both plain and rich modes. Disable with
enableEmoji={false}. - Rich formatting — a plain textarea by default; the
Aatoggle switches to the markdown editor. The mode is authoring-only — the stored value is always markdown and always rendered as markdown, so a comment written in plain mode and one written in rich mode display identically. Control availability withenableRichFormatting, the initial mode withdefaultRichFormatting, or persist the user's choice with the controlledrichFormatting/onRichFormattingChangepair.
Attachments
The composer takes files three ways: the paperclip button, pasting (a copied screenshot lands on the clipboard as a file, not as text), and dragging them onto the composer, which shows a drop target while a file drag is over it. All three land in the same pending list under the input and reach onCreateComment as attachments — forward that array to your upload/create call, or the picked files are silently dropped on send.
API Reference
| Prop | Type | Default |
|---|---|---|
comments | DocyrusComment[] | — |
currentUser | CommentUser | — |
users | CommentUser[] | — |
title | string | "Comments" |
editable | boolean | true |
isLoading | boolean | false |
maxHeight | number | string | '24rem' |
onCreateComment | ({ message, parentId?, attachments?, mentions? }) => void | Promise<void> | — |
onUpdateComment | (id: string, message: string, mentions?: string[]) => void | Promise<void> | — |
onDeleteComment | (id: string) => void | Promise<void> | — |
onUploadFile | (file: File) => Promise<CommentAttachment> | — |
isCreatePending | boolean | false |
isDeletePending | boolean | false |
enableRichFormatting | boolean | true |
defaultRichFormatting | boolean | false |
richFormatting | boolean | — (controlled) |
onRichFormattingChange | (rich: boolean) => void | — |
enableEmoji | boolean | true |
enableMentions | boolean | true |
className | string | — |
Components
| Component | Description |
|---|---|
CommentsPanel | Public export |
CommentComposer | Internal — comment-composer.tsx (create / reply / edit) |
CommentEmptyState | Internal — comment-empty-state.tsx |
CommentItem | Internal — comment-item.tsx |
CommentThread | Internal — comment-thread.tsx |
MentionList | Internal — mention-list.tsx |
useCommentMention | Internal — use-comment-mention.tsx |
Type Exports
| Type | Description |
|---|---|
CommentsPanelProps | — |
DocyrusComment | — |
CommentThread | — |
CommentAttachment | — |
CommentUser | — |
Type Reference
DocyrusComment
| Field | Type | Description |
|---|---|---|
id | string | — |
message | string | — |
attachments | Array<CommentAttachment> | null | — |
created_on | string | ISO date string |
last_modified_on | string | ISO date string |
created_by | string | user id |
record_id | string | null | — |
parent_id | string | null | reply to |
CommentAttachment
| Field | Type | Description |
|---|---|---|
id | string | — |
file_name | string | — |
file_type | string | MIME type |
file_size | number | bytes |
signed_url | string | null | download URL |
CommentUser
| Field | Type | Description |
|---|---|---|
id | string | — |
firstname | string | null | — |
lastname | string | null | — |
photo | string | null | avatar image URL (mention list + comment avatars) |
CommentThread
| Field | Type | Description |
|---|---|---|
comment | DocyrusComment | — |
replies | Array<DocyrusComment> | — |