27.7k

Kanban

A drag-and-drop kanban board with columns, cards, and keyboard navigation for organizing tasks and workflows

Import

import { Kanban, useKanban, useKanbanColumn, useKanbanCardPlaceholder } from '@darkcode-ui/react';

Anatomy

The board is composed from compound parts. State is managed by the useKanban hook, while each column wires its drag-and-drop behaviour with useKanbanColumn.

<Kanban>
  <Kanban.Column>
    <Kanban.ColumnHeader>
      <Kanban.ColumnIndicator />
      <Kanban.ColumnTitle />
      <Kanban.ColumnCount />
      <Kanban.ColumnActions />
    </Kanban.ColumnHeader>
    <Kanban.ColumnBody>
      <Kanban.CardList>
        <Kanban.Card>
          <Kanban.DragHandle />
        </Kanban.Card>
      </Kanban.CardList>
    </Kanban.ColumnBody>
  </Kanban.Column>
</Kanban>

Usage

Cards can be dragged between columns with a pointer or the keyboard (focus a card, then activate its drag handle). The board keeps all items in a single list and filters them per column.

"use client";

import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react";
import type {CSSProperties} from "react";

Notion Board

"use client";

import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react";
import type {CSSProperties} from "react";

Project Board

"use client";

import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react";
import type {CSSProperties} from "react";

Sizes

The size prop scales column width, card padding, and font size. Use sm, md (default), or lg.

"use client";

import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react";
import type {CSSProperties} from "react";

Styling

Passing Tailwind CSS classes

Every part accepts a className, and the colored status dot reads the --kanban-indicator-color custom property:

<Kanban.Column className="snap-start">
  <Kanban.ColumnHeader>
    <Kanban.ColumnIndicator style={{ '--kanban-indicator-color': 'var(--success)' }} />
    <Kanban.ColumnTitle className="tracking-tight">Done</Kanban.ColumnTitle>
  </Kanban.ColumnHeader>
</Kanban.Column>

Customizing the component classes

To customize the Kanban component classes, you can use the @layer components directive.
Learn more.

@layer components {
  .kanban {
    --kanban-column-min-width: 320px;
    --kanban-column-gap: 24px;
  }

  .kanban__card-content {
    @apply shadow-none ring-1 ring-border;
  }
}

DarkCode UI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The Kanban component uses these CSS classes (View source styles):

Base & Size Classes

  • .kanban - Root board container. CSS grid with horizontal auto-flow and snap scrolling.
  • .kanban--sm - Small size. Narrower columns (240px), tighter gap (12px).
  • .kanban--md - Medium size (default). Standard columns (280px), default gap (16px).
  • .kanban--lg - Large size. Wider columns (320px), spacious gap (20px).

Element Classes

  • .kanban__column - Individual column section.
  • .kanban__column-body - Visual container for the card list and footer actions.
  • .kanban__column-header - Header area with indicator, title, count, and actions.
  • .kanban__column-actions - Hover-reveal action buttons inside the header.
  • .kanban__column-indicator - Colored status dot (when empty) or icon container (with children).
  • .kanban__column-title - Column heading text (text-sm font-semibold).
  • .kanban__column-count - Item count badge (text-xs text-muted).
  • .kanban__card - Outer card wrapper. Handles focus ring, cursor, and drag opacity.
  • .kanban__card--sm, .kanban__card--md, .kanban__card--lg - Card size variants.
  • .kanban__card-content - Inner Motion wrapper carrying background, radius, shadow, and layout.
  • .kanban__card-content--sm, .kanban__card-content--md, .kanban__card-content--lg - Content size variants.
  • .kanban__card-list - Scrollable GridList container with vertical gap between cards.
  • .kanban__scroll-shadow - Optional ScrollShadow wrapper for constrained, scrollable columns.
  • .kanban__drop-indicator - Custom drop indicator with animated height via --kanban-drop-height.
  • .kanban__drag-handle - Grip button, screen-reader-only by default. Reveals on focus.
  • .kanban__empty - Empty state placeholder centered inside an empty column.

Interactive States

  • .kanban__card:hover .kanban__card-content - applies shadow-sm.
  • .kanban__card[data-selected="true"] .kanban__card-content - applies bg-accent-soft.
  • .kanban__card[data-dragging] - reduced opacity.
  • .kanban__card[data-focus-visible="true"] - focus ring with ring-2.
  • .kanban__column-header:hover .kanban__column-actions - opacity transitions to 1.
  • .kanban__drop-indicator[data-drop-target] - border and animated height.

CSS Variables

  • --kanban-column-min-width - Minimum column width (default: 280px).
  • --kanban-column-gap - Gap between columns (default: 16px).
  • --kanban-column-height - Column height reference (default: 480px).
  • --kanban-drop-height - Drop indicator placeholder height. Set automatically by useKanbanCardPlaceholder.
  • --kanban-indicator-color - Color of the column status dot.

API Reference

Kanban

The root board container. Wraps children in a horizontal ScrollShadow.

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Size variant controlling card padding, font size, and column width.

Also supports all ScrollShadow props (except size).

Kanban.CardList

The scrollable card list backed by React Aria GridList.

PropTypeDefaultDescription
selectionMode"none" | "single" | "multiple""none"Selection behavior for cards.
renderEmptyState() => ReactNode—Custom empty state content when the column has no cards.
dragAndDropHooksDragAndDropHooks—The hooks returned by useKanbanColumn.

Also supports all React Aria GridList props.

Kanban.Card

An individual draggable card. Wraps content in a Motion layout animation. Backed by React Aria GridListItem.

PropTypeDefaultDescription
textValuestring—Accessible text value for the card.
contentClassNamestring—Class names for the inner Motion content surface.
childrenReactNode | ((renderProps) => ReactNode)—Card content. May be a render function receiving GridListItem render props.

Also supports all React Aria GridListItem props.

Kanban.DropIndicator

Visual drop indicator shown during drag operations.

PropTypeDefaultDescription
targetDropTarget—The drop target provided by React Aria.
heightnumber—Height of the placeholder in pixels. Typically the dragged card's height via --kanban-drop-height.

Kanban.DragHandle

Grip button for keyboard-accessible dragging. Screen-reader-only by default; reveals on focus. Uses slot="drag".

Also supports all React Aria Button props.

Kanban.ScrollShadow

Optional scroll wrapper for constrained column heights. Use with max-h-[...] to enable vertical scrolling within a column.

Also supports all ScrollShadow props.

Other Parts

Kanban.Column, Kanban.ColumnHeader, Kanban.ColumnBody, Kanban.ColumnActions, Kanban.ColumnIndicator, Kanban.ColumnTitle, and Kanban.ColumnCount are styling wrappers that render section, header, div, h3, and span elements respectively and accept standard HTML props.

useKanban

Manages the shared list data for the entire board.

const kanban = useKanban({
  initialItems: tasks,
  getColumn: (item) => item.status,
  setColumn: (item, column) => ({ ...item, status: column }),
});
OptionTypeDefaultDescription
initialItemsT[]—Items to populate the board with.
getColumn(item: T) => string—Return the column identifier for a given item.
setColumn(item: T, column: string) => T—Return a copy of the item assigned to a new column.
dragTypestring"kanban-item-id"Custom drag type for transferring item keys between columns.
getKey(item: T) => string | numberitem.idDerive a unique key from each item.

Returns: { list, addItem, removeItem, moveItem, updateItem, getColumn, setColumn, getKey, dragType }.

useKanbanColumn

Provides filtered items and drag-and-drop hooks for a single column.

const { items, dragAndDropHooks } = useKanbanColumn(kanban, "in-progress", {
  renderDropIndicator,
});
OptionTypeDefaultDescription
kanbanUseKanbanReturn<T>—The kanban instance from useKanban.
columnstring—The column identifier to filter items by.
options.renderDropIndicator(target) => ReactElement—Override the default drop indicator rendering.

Returns: { items, dragAndDropHooks } — pass dragAndDropHooks to Kanban.CardList.

useKanbanCardPlaceholder

Measures the dragged card's height and writes it as a CSS variable for card-sized drop indicators.

const { renderDropIndicator } = useKanbanCardPlaceholder({
  renderIndicator: (target) => <Kanban.DropIndicator target={target} />,
});
OptionTypeDefaultDescription
renderIndicator(target: DropTarget) => ReactElement—Render function that returns a custom Kanban.DropIndicator.

Returns: { renderDropIndicator } — pass to useKanbanColumn options.

On this page