# Comments Panel URL: /docs/web/components/comments-panel Comments Panel component. **Demo:** ```tsx 'use client'; // @custom-demo import { useState } from 'react'; import { CommentsPanel } from '@docyrus/ui/components/comments-panel'; import { Button } from '@docyrus/ui/primitives/ui/button'; import { cn } from '@docyrus/ui/primitives/lib/utils'; import { PropControls } from '@/components/prop-controls'; import { useDemoData } from '@/data/comments-panel-data'; export function CommentsPanelDemo() { const { props, controls, actions } = useDemoData(); const [open, setOpen] = useState(false); return (
); } ``` 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 ```bash pnpm dlx @docyrus/cli add @docyrus/ui-comments-panel ``` **Dependencies:** - [react-markdown](https://www.npmjs.com/package/react-markdown) - [remark-gfm](https://www.npmjs.com/package/remark-gfm) - [date-fns](https://www.npmjs.com/package/date-fns) - [lucide-react](https://www.npmjs.com/package/lucide-react) Composes the [Emoji Picker](/docs/web/components/emoji-picker) and Simple Markdown Editor components — the CLI installs them automatically as registry dependencies. ## Usage ```tsx 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 ( 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 the `users` prop. The selected user is stored inline as a markdown link (`[@Name](mention://)`) and rendered as a highlighted chip. `onCreateComment` / `onUpdateComment` receive the referenced user ids in `mentions`. Disable with `enableMentions={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 `Aa` toggle 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 with `enableRichFormatting`, the initial mode with `defaultRichFormatting`, or persist the user's choice with the controlled `richFormatting` / `onRichFormattingChange` pair. ## 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` | — | | `onUpdateComment` | `(id: string, message: string, mentions?: string[]) => void \| Promise` | — | | `onDeleteComment` | `(id: string) => void \| Promise` | — | | `onUploadFile` | `(file: File) => Promise` | — | | `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 \| 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` | — |