shadcn.io is an independent project built on shadcn/ui
Cards sit in columns, one column per stage, and move between them by drag or by keyboard.
Use it when work passes through a few fixed stages and people move it themselves, like a sprint or a hiring pipeline. Items you sort and compare belong in a Table, and one column of grouped rows is a List. In Solid, the board writes its own pointer and key handling, so there is no drag-and-drop package to install, and Ark UI isn't involved in the move.
Installation
Anatomy

KanbanProvider
โโโ KanbanBoard
โโโ KanbanHeader
โโโ KanbanCards
โโโ KanbanCardUsage
import {
KanbanBoard, KanbanCard, KanbanCards, KanbanHeader, KanbanProvider,
} from "@/components/ui/kanban";const columns = [
{ id: "todo", name: "To Do" },
{ id: "done", name: "Done" },
];
const [tasks, setTasks] = createSignal([
{ column: "todo", id: "1", name: "Write the release notes" },
{ column: "done", id: "2", name: "Rotate the API keys" },
]);
<KanbanProvider columns={columns} onValueChange={setTasks} value={tasks}>
{columns.map((column) => (
<KanbanBoard id={column.id} key={column.id}>
<KanbanHeader>{column.name}</KanbanHeader>
<KanbanCards>
{tasks
.filter((task) => task.column === column.id)
.map((task) => (
<KanbanCard id={task.id} key={task.id} name={task.name} />
))}
</KanbanCards>
</KanbanBoard>
))}
</KanbanProvider>The items are one flat array: each has an id, a name and the column it sits in, and their order in the array is their order in each column. A move hands the whole array back through onValueChange, so the board is always controlled.
Accessibility
Each card is focusable and says how to move it. A polite live region announces every pick-up, step, drop and cancel with the card's name, its column and its position.
- Space
- Pick up the focused card, or drop the card you are holding.
- Enter
- Pick up the focused card, or drop the card you are holding.
- Arrow Up
- Move the held card up one place in its column.
- Arrow Down
- Move the held card down one place in its column.
- Arrow Left
- Move the held card to the previous column, or the next one in a right-to-left layout.
- Arrow Right
- Move the held card to the next column, or the previous one in a right-to-left layout.
- Home
- Move the held card to the top of its column.
- End
- Move the held card to the bottom of its column.
- EscEscape
- Cancel the move and put the card back where it was picked up.
- Tab
- Drop the held card where it is and move focus on.
Demos
Custom Cards
A roadmap with an owner's avatar, a date range and a Badge on every card, and a colored dot and a count in each header. KanbanCard takes any children in place of its name, and the whole card is still the handle, except for links, buttons and fields inside it, which keep their own clicks. Keep the card short: a board is for scanning stages, and the details belong in a dialog or a page.
Move Events
The line under the board shows what onMoveStart and onMoveEnd report: the card's id, where it came from, and where it went to, which is null when the move was cancelled. Both fire for the pointer and the keyboard. Use them to save a move to your server or to log it, and keep onValueChange for the array itself.
Scrolling
Twelve issues on a board with h-80 on KanbanProvider and min-h-0 on each KanbanBoard, so each column scrolls on its own and the board keeps its height. Drag a card toward a column's top or bottom edge and the column scrolls under it; when the columns are wider than the board, it scrolls sideways the same way. Give the board a height whenever a column can grow past the screen.
FAQ
What data does the Kanban board need?
Two arrays. columns lists each column's id and name, in order. value lists every item with its own id, a name and the column id it sits in. The order of the items in that one array is their order within each column, so a move hands back the whole array.
How do I save where a card was moved in Solid?
onValueChange gets the new array after every move: set your state from it, and the board redraws. To send the move to a server, read onMoveEnd, which says which card moved, from where and to where, once per drop. The Move Events demo prints both.
Can people move cards without a mouse?
Yes. Tab to a card, press Space or Enter to pick it up, move it with the arrow keys (left and right change columns, up and down change its place), then press Space or Enter to drop it or Escape to put it back. A live region announces each step with the column and the position.
Does the Kanban board work on touch screens?
Yes. A finger held on a card for a moment picks it up, and from then on the page stays still while the card follows. A quick swipe still scrolls the columns, so a long board stays easy to read on a phone.
How do I put more than a title on a card?
Pass children to KanbanCard: they replace its name, as in the Custom Cards demo. Keep the name prop anyway, because the announcements read it. A button or a link inside a card keeps its click, and anything marked data-kanban-no-drag doesn't start a drag either.
How do I change how a dragged card looks?
Each state is a data attribute in the kanban file under components/ui. The card left in place has data-held and dims; the copy under the pointer is the kanban-card-overlay slot, tilted with a ring; a card held from the keyboard has data-lifted; the column under it has data-over; and data-drop draws the line where it would land. Change those classes once and every board follows.
Should I use Kanban or List?
Use Kanban when stages sit side by side and people move work across them. Use a List for one column of groups, such as tasks under To Do, In Progress and Done in a narrow panel. Both take the same flat array and move the same way.
API Reference
KanbanProvider
valueT[]
The items, in order. Render each column's cards from it.
onValueChange?(value: T[]) โ void
Called with every item, in order, after each move. Set value from it.
- Parameters
valueโ T[]- Returns
void
columnsKanbanColumnData[]
The columns, in order: a KanbanBoard for each. Arrow keys move a lifted card along them.
onMoveEnd?(details: KanbanMoveDetails) โ void
Called when a card is dropped, or its move is cancelled (to is null).
- Type
(details: KanbanMoveDetails) โ void- Parameters
detailsโ { from: KanbanSpot; id: string; to: KanbanSpot | null }- Returns
void
onMoveStart?(details: KanbanMoveDetails) โ void
Called when a card is picked up, by the pointer or the keyboard.
- Type
(details: KanbanMoveDetails) โ void- Parameters
detailsโ { from: KanbanSpot; id: string; to: KanbanSpot | null }- Returns
void
class?string
Merged with the part's classes.
KanbanBoard
<div> data-slot="kanban-board"
idstring
The id of the column this board shows.
class?string
Merged with the part's classes.
data-kanban-column?string
Mirrors id.
data-over?stringKanbanHeader
<div> data-slot="kanban-header"
class?string
Merged with the part's classes.
KanbanCards
<div> data-slot="kanban-cards"
class?string
Merged with the part's classes.
data-kanban-list""
- Default
""
KanbanCard
data-slot="kanban-card"
idstring
The item's id.
namestring
The item's name: the card's content when it has no children.
class?string
Merged with the part's classes.
data-drop?stringdata-held?string
Present when held is true.
data-kanban-card?string
Mirrors id.
data-lifted?string









