Trello-style board with draggable cards and lists on one horizontal track. The board fills 100% of its parent's width and height. Lists grow with their cards up to the available board height, then their card area scrolls while the add-card control stays pinned to the bottom. The prominent board title and tag manager sit above the lists, and the left/right scroll controls are always available. Click the board title to rename it. Lists can be created, renamed, recoloured and reordered; cards support inline creation, comments, tags and a two-column ticket view.
| Prop | Type | Default | Description |
|---|---|---|---|
boardId | string | — | Optional persistent id included in board deletion callbacks. |
boardTitle | string | “Kanban board” | Board name shown above the lists. Click it to rename inline. |
onBoardTitleChange | (title) => void | — | Called when the board name is edited. Omit boardTitle to let the board manage its own title. |
onBoardDelete | `({ id, title }) => void | Promise<void>` | — |
columns | KanbanColumn[] | required | Lists and their card ids. |
cards | Record<string, KanbanCard> | required | Card lookup. New cards are also held locally until the parent responds. |
onChange | (columns: KanbanColumn[]) => void | — | Enables card moves, list reordering, list creation and list edits. |
onColumnCreate | (column, columns) => void | — | Called after a list is created. |
onColumnChange | (column, previousColumn, columns) => void | — | Called after a title or colour edit. |
onColumnReorder | (columns, { columnId, fromIndex, toIndex }) => void | — | Called after a list is reordered. |
onCardCreate | (card, columnId, columns, cards) => void | — | Called after a card is created inline. |
onCardMove | (cardId, { fromColumnId, fromIndex, toColumnId, toIndex }, columns) => void | — | Called after a card is moved. |
onCardUpdate | (cardId, patch, card) => void | — | Called when ticket title, description or header colour changes. |
columnColors | string[] | Built-in palette | Shared by list headers, ticket headers and tags. |
columnWidth | number | string | 280 | Fixed width for every list. |
addColumnLabel / addCardLabel | string | “Add list” / “Add a card” | Composer labels. |
tagManager | boolean | true | Shows the board-level tag button and manager, including when there are no tags. |
ticketView | boolean | true | Enables the two-column ticket view. |
activeCardId | string | null | — | Controlled open ticket. |
defaultActiveCardId | string | null | — | Initial ticket when uncontrolled. |
onActiveCardChange | (cardId, card) => void | — | Called when the ticket opens, closes, or the active card changes. |
onCardClick | (card) => void | — | Optional external card-body click handler; otherwise the ticket opens. |
onActivity | (activity: KanbanActivity) => void | — | Single structured event stream for persistence. |
The ticket modal uses a two-column layout:
Descriptions and comments use Opus RichTextField. Authors can format text, insert links and tables, and attach files or paste screenshots directly into the content. Inline files are uploaded through the same attachment adapter and embedded as image tags or download links.
Selected tags are shown as removable chips; Add tag opens a searchable modal where new tags can also be created. Add member opens a searchable member picker with assign/unassign toggles. Pass onSearchMembers to search an API for active users while typing; without it the picker filters the members already on the board.
| Prop | Type | Description |
|---|---|---|
commentsEnabled | boolean | Shows the comment thread inside the ticket. |
currentUser | { name: string } | Author shown in the composer. |
onAddComment | (cardId, comment) => void | Called when a comment is added. The comment includes any attachments. |
onCommentsChange | (cardId, comments) => void | Full thread after a change. |
tags / defaultTags | KanbanTag[] | Controlled or initial board tags. |
onTagsChange | (tags) => void | Called after tag create, update or delete. |
onTagCreate | (tag, tags) => void | Called after a tag is created. |
onTagUpdate | (tag, previousTag, tags) => void | Called after a tag rename or recolour. |
onTagDelete | (tag, tags) => void | Called after a tag is deleted. |
onCardTagsChange | (cardId, tagIds, card) => void | Called when tags are added or removed from a ticket. |
members / defaultMembers | KanbanMember[] | Controlled or initial board members. |
onSearchMembers | (query, card) => Promise<KanbanMember[]> | KanbanMember[] | Searches members remotely while the picker search changes, replacing the local name and role filter. Use it to query active users from your API: the empty query arrives when the picker opens, so return a default page of people. Members returned by a search stay available for avatar and chip rendering. |
memberSearchDebounceMs | number | Delay after typing before onSearchMembers runs. Defaults to 250. |
onCardAssigneesChange | (cardId, memberIds, card) => void | Called when members are assigned or unassigned. |
onAttachmentUpload | (file, context) => Promise<KanbanAttachment> | Uploads a ticket, description, or comment file. Return the persisted attachment with its final URL. Inline editor uploads use the same adapter. |
onAttachmentRemove | (attachment, context) => void | Promise<void> | Deletes a ticket attachment, or a finished comment-draft attachment, from storage. |
onCardAttachmentsChange | (cardId, attachments, card) => void | Full ticket attachment list after an attachment is added or removed. Persist this list. When onAttachmentUpload is supplied it fires after the upload resolves, never with local blob URLs. |
Ticket attachments and comment attachments both use the same KanbanAttachment shape. Without onAttachmentUpload, the board uses in-session blob: previews for demos. In production, provide the adapter and persist only the ready attachments you receive.
context.target is "card" for ticket attachments, "description" for inline description files, and "comment" for comment composer files. Comment files upload immediately, so a cancelled draft can leave an orphan until onAttachmentRemove runs. The board blocks comment submission while files upload or after a failure.
Inline images and files are stored inside the rich HTML rather than in onCardAttachmentsChange. Use the upload adapter to persist the file itself; removing an inline image from the editor does not automatically delete the stored file.
Use url for opening and downloadUrl when the backend provides a Content-Disposition download endpoint. If no downloadUrl is supplied, the board downloads using url.
KanbanCard supports title, meta, rich HTML description, color, tone, comments, tagIds, assigneeIds and attachments. The color value is the ticket side-border colour. KanbanComment.body is also rich HTML.
KanbanActivity is a discriminated union covering: list-created, list-updated, list-reordered, card-created, card-moved, card-updated, comment-added, tag-created, tag-updated, tag-deleted, card-tags-changed, card-assignees-changed and card-attachments-changed. Every event includes an ISO at timestamp and the relevant records or resulting collections.
The settings button sits beside the Tags button. It opens a board-settings modal intended to grow with member access and workspace controls.
The Danger zone requires the exact board name before Delete board becomes available. The board does not remove itself; connect onBoardDelete to delete the board and navigate away. Async handlers keep the modal open and surface an error if deletion fails.
| Prop | Type | Default | Description |
|---|---|---|---|
cardClickAction | string | modal | Configures card click action. |
interactive | boolean | true | Configures interactive. |
Copy this complete component into a page file. Edit and run it in the Playground.
Loading editor…