Components

Kanban

Pronew

A drag-and-drop board of sortable columns and cards, operable by pointer, touch and keyboard.

Backlog2
BLK-112MS

Add RTL support to the sidebar

Feature
BLK-118AK

Write the theming guide

Docs
In progress3
BLK-104DK

Focus ring disappears in dark mode

Bug
BLK-109MS

Keyboard support for the date picker

Feature
BLK-097JL

Combobox loses selection on blur

Bug
Done1
BLK-090AK

Migrate tokens to the semantic layer

Feature

Installation

npx shadcn@latest add @blank-pro/kanban

Needs a Blank Pro licence key and the @blank-pro registry. Set up Pro access

Usage

import {
  Kanban,
  KanbanBoard,
  KanbanColumn,
  KanbanColumnHandle,
  KanbanItem,
  KanbanItemHandle,
  KanbanOverlay,
} from "@/components/ui/kanban"
const [columns, setColumns] = React.useState({
  backlog: [{ id: "1", title: "Write the theming guide" }],
  done: [],
})

<Kanban
  value={columns}
  onValueChange={setColumns}
  getItemValue={(task) => task.id}
  getItemLabel={(task) => task.title}
>
  <KanbanBoard>
    {Object.entries(columns).map(([column, tasks]) => (
      <KanbanColumn key={column} value={column}>
        {tasks.map((task) => (
          <KanbanItem key={task.id} value={task.id} asHandle>
            <TaskCard task={task} />
          </KanbanItem>
        ))}
      </KanbanColumn>
    ))}
  </KanbanBoard>
  <KanbanOverlay>
    {({ value, variant }) => …}
  </KanbanOverlay>
</Kanban>

value is the whole board: column keys in display order, each mapped to its items in order. Every move — within a column, across columns, or of a column itself — comes back through onValueChange as a new map, so persisting the board is one setState or one request. Items can be strings or objects; for objects, getItemValue returns the id.

KanbanOverlay renders the copy that follows the pointer, so the card can leave its column without being clipped. Its render prop gets the active id and whether it is a column or an item; render the same card you render on the board and it is styled as lifted. The original stays behind as a dashed slot. Pressing Escape mid-drag puts the card back where it started, even after it has crossed into another column.

Cards are draggable by keyboard: focus a card or handle, press Space, move with the arrow keys and press Space again. Screen readers hear where it is at every step — "Focus ring disappears in dark mode is now at position 2 of 3 in Done" — when you name things with getItemLabel and getColumnLabel.

asHandle makes a whole card or column the drag handle. For cards with their own buttons or checkboxes, put a KanbanItemHandle inside instead, as below.

Examples

Vertical, with handles

orientation="vertical" stacks the columns — sections of a list here — and KanbanItemHandle leaves the rest of each row free for its own controls.

Today
Review the dialog PR
Reply to the token naming thread
This week
Pair on the data grid
Record the theming walkthrough
Later
Audit icon sizes in menus

API Reference

Kanban

Takes DndContext props (sensors, modifiers, onDragStart, onDragEnd…) except collisionDetection, plus:

PropTypeDefault
valueRecord<UniqueIdentifier, T[]>—
defaultValueRecord<UniqueIdentifier, T[]>{}
onValueChange(columns) => void—
onMove(event & { activeIndex, overIndex }) => void—
getItemValue(item: T) => UniqueIdentifierthe item itself
getItemLabel(item: T) => stringthe id
getColumnLabel(column) => stringthe key
orientation"horizontal" | "vertical""horizontal"

onMove, when given, replaces onValueChange for reorders that finish on drop — useful when the server decides the order.

KanbanBoard

div props, plus asChild.

KanbanColumn

PropTypeDefault
valueUniqueIdentifier—
asHandlebooleanfalse
asChildbooleanfalse
disabledbooleanfalse

Carries data-dragging while it is being moved and data-drop-target while it holds the card being dragged.

KanbanItem

PropTypeDefault
valueUniqueIdentifier—
asHandlebooleanfalse
asChildbooleanfalse
disabledbooleanfalse
childrenReactNode | ({ dragging, overlay }) => ReactNode—

Carries data-dragging on the slot left behind and data-overlay on the copy in KanbanOverlay.

KanbanColumnHandle, KanbanItemHandle

button props, plus asChild.

KanbanOverlay

DragOverlay props, plus:

PropTypeDefault
containerElement | DocumentFragment | nulldocument.body
childrenReactNode | ({ value, variant }) => ReactNode—

With prefers-reduced-motion, the drop animation is skipped and the lifted copy does not tilt.