Component

Kanban

<DomKanban>

A controlled, API-shaped Kanban for shared resource rows, custom Vue card renderers, async creation, and precise card or column ordering.

Demo

A practical todo board

Drag anywhere over a column to target its nearest insertion point; the board opens a card-sized placeholder so empty columns and exact ordering remain obvious. On drop, the destination is committed immediately while the dimmed source card folds closed. Hold near the top or bottom of a long column to auto-scroll it. Reorder columns by their grip, or add local columns and cards. The createCard hook emulates a remote POST and returns the server-owned id before update:rows is emitted.

Todo board

To do

2

Ideas and unstarted work.

Doing

1/3

Work currently in progress.

Done

1

Completed this week.

Drag a card, reorder a column, or add a todo.

Shared resources

One list across Kanban, grid, and calendar

The view switch belongs to the application because each view may have different query, pagination, and editing rules. The underlying feature records do not change shape: status and position drive Kanban, status remains a normal grid column, and dueDate drives the calendar.

Product features

Backlog

1

Planned

2

Active

2/3

Complete

1

All three views receive the same resources array. Kanban maps status and position; the grid exposes status as a select column; the calendar maps dueDate.

shared-resource-views.vuevue

Custom cards

Application-defined feature cards

Pass cardComponent for a reusable application card, set component on an individual column, or use the card slot for inline control. DomKanban retains ordering, keyboard movement, focus, and drag semantics around the custom renderer.

Product delivery board

Discovery

1

Validate the problem and expected outcome.

Delivery

2

Design and implementation in progress.

Validation

1

QA, rollout, and customer feedback.

Custom cards can emit application actions without owning navigation.

ProjectFeatureCard.vuevue

Architecture

Controlled data and transport boundaries

DomKanban never sends a request and never mutates a supplied row. A successful interaction emits an immutable update:rows projection for local or optimistic state plus a detailed card-move payload for persistence. Use v-model:rows for a local board, or listen to the payload and reconcile it through a store for server data.

The move payload includes minimal changes for the moved row and complete key-only orders for every affected column. A backend can update the status field and all affected positions in one transaction without reconstructing intent from neighbouring records.

Card and column creation use optional promise-aware hooks because the component sometimes needs a server id before it can publish the next controlled array. A hook can POST the suggested record and return the complete saved resource or only its id. Rejections leave the composer open and emit a typed error event.

View orchestration stays above the components. A data grid may paginate a million rows remotely, while a Kanban usually requests one scoped board and a calendar requests a date window. Keeping the switcher application-owned avoids pretending those queries are interchangeable while preserving one resource schema.

kanban-resource-api.httphttp

Reference

Props

Control props

NameTypeTSDefaultDescription
rowsarrayArray<unknown>[]Resource rows shared with a grid, calendar, API, or application store.
columnsarrayArray<KanbanColumn
type KanbanColumn = {
	key: string | number; // Stable value stored on each row by columnKey.
	label?: string; // Visible column label.
	description?: string; // Optional supporting text below the label.
	limit?: number; // Maximum cards accepted from another column.
	component?: object | Function | string; // Vue component used to render cards in this column.
	disabled?: boolean; // Disable card creation, card drops, and column reordering.
	addable?: boolean; // Set false to hide the built-in Add card control for this column.
	canDrop?: Function; // Function that receives a drop context and decides whether the column accepts it.
	class?: string | Array | object; // Classes applied to the column shell.
	cardClass?: string | Array | object; // Classes applied to card wrappers in the column.
};
>
[]Ordered Kanban column definitions. Keys should match values stored by columnKey.
rowKeystring | functionstring'id'Field name or function used to identify each resource row.
columnKeystringstring'status'Row field that assigns a resource to a Kanban column.
orderKeystringstring'position'Row field that stores the zero-based order within a column.
titleKeystring | functionstring'title'Field name or function used by the built-in card renderer for its title.
descriptionKeystring | functionstring'description'Field name or function used by the built-in card renderer for supporting text.
cardComponentobject | function | stringRecord<string, unknown>Vue component used to render every card. A column component or card slot can override it.

Creation

NameTypeTSDefaultDescription
createCardfunctionFunctionOptional async hook that persists a suggested row and returns a row or stable id.
createColumnfunctionFunctionOptional async hook that persists a suggested column and returns a column or stable key.
allowAddCardsbooleanbooleantrueShow a compact title composer at the bottom of each enabled column.
allowAddColumnsbooleanbooleantrueShow a column composer after the final board column.

Interaction

NameTypeTSDefaultDescription
dragCardsbooleanbooleantrueAllow native pointer drag/drop and Alt+Arrow keyboard card movement.
reorderColumnsbooleanbooleantrueAllow columns to be reordered with the grip or Alt+Left/Right.
cardDraggableboolean | functionbooleantrueBoolean or function that decides whether an individual card can move.
canDropfunctionFunctionOptional application rule that receives source and target context before a drop.

Labels

NameTypeTSDefaultDescription
boardLabelstringstring'Kanban board'Accessible name for the board region.
resourceLabelstringstring'cards'Human label used in card counts and announcements.
addCardLabelstringstring'Add card'Label for each column creation control.
addColumnLabelstringstring'Add column'Label for the board column creation control.
emptyTextstringstring'Drop a card here or add a new one.'Default text shown in an empty column.

Layout

NameTypeTSDefaultDescription
columnWidthstring | numberstring'18rem'Fixed width of each horizontally scrolling column.
heightstring | numberstring'36rem'Height of the board viewport.

State

NameTypeTSDefaultDescription
disabledbooleanbooleanfalseDisable creation and reordering while keeping the board readable.
loadingbooleanbooleanfalseShow a non-blocking loading indicator over the board.

Auto-generated from Kanban.props and inline _edit hints.

Events

NamePayloadDescription
@update:rowsArray<Record<string, unknown>>Immutable local projection after a completed card move or creation. Use v-model:rows for local boards.
@update:columnsArray<KanbanColumn>Immutable ordered columns after a completed reorder or creation.
@card-moveKanbanCardMoveFired after a valid pointer or keyboard move. Persist changes and orders to a resource API.
@card-dropKanbanCardMoveAlias emitted with card-move for applications that model drag/drop events.
@card-create-request({ column, columnKey, index, draft, suggestedRow })Fired before the optional createCard hook runs.
@card-create({ column, columnKey, index, draft, result, row, nextRows })Fired when the creation hook succeeds or a local suggested row is accepted.
@card-create-error({ column, columnKey, draft, error })Fired when createCard rejects.
@column-move({ column, columnKey, sourceIndex, targetIndex, columnKeys, nextColumns, input })Fired after a valid pointer or keyboard column reorder.
@column-create-request({ label, suggestedColumn })Fired before the optional createColumn hook runs.
@column-create({ label, result, column, nextColumns })Fired when the column creation hook succeeds or a local suggestion is accepted.
@column-create-error({ label, error })Fired when createColumn rejects.
@card-click({ row, rowKey, column, columnKey, index, event })Fired when a card shell is activated without using an inner interactive control.
@card-action({ row, rowKey, column, columnKey, index, action, payload })Re-emitted when a custom card component emits action.
@card-drag-start({ row, rowKey, source, event })Fired when native card dragging begins.
@card-drag-end({ row, rowKey, source, event })Fired when native card dragging ends.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Slots

NameScopeDescription
#card{ row, rowKey, column, columnKey, index, drag }Custom card renderer. The component retains the accessible focus and drag shell.
#column-header{ column, columnKey, index, rows, count, limit }Custom content for a column header.
#empty{ column, columnKey }Custom empty-column state.
#card-composer{ column, columnKey, draft, setDraft, submit, cancel, creating }Custom card creation form.
#column-composer{ draft, setDraft, submit, cancel, creating }Custom column creation form.
#column-footer{ column, columnKey, rows, count }Extra content below a column card list.

Keyboard

  • Alt + Up / DownMove the focused card within its current column.
  • Alt + Left / RightMove the focused card into the adjacent column.
  • Alt + Left / Right on column gripReorder the current column.
  • EnterSubmit the built-in card or column composer.
  • EscapeClose the active composer.