Board

A data-driven kanban board: columns scroll horizontally, each column is a drop zone, and every card carries a drag grip, an optional trailing badge, a muted two-line description, a free-form chips slot, and an optional kebab menu.
Dragging is the kit's own drag-and-drop family, so it runs on iOS, Android, and the web from one code path and stays keyboard- and screen-reader-operable: press Space on a grip to grab, the arrow keys to move between positions and columns, Space to drop, Escape to cancel. The list stays controlled: a drop reports a BoardMove (source, target, insertion index, and the new neighbor ids) through onMove, and applyBoardMove is the standard reducer to apply it. Pass defaultItems instead of items for uncontrolled use, where the board applies each move itself.
To do
2
Rotate webhook secrets
Draft the design review
Doing
1
SSO rollout
Done
1
Upgrade the CI runners
iOS
To do
2
Rotate webhook secrets
Draft the design review
Doing
1
SSO rollout
Done
1
Upgrade the CI runners
Android
To do
2
Rotate webhook secrets
Draft the design review
Doing
1
SSO rollout
Done
1
Upgrade the CI runners
Web
tsx
<Board
columns={[
{ id: "todo", label: "To do" },
{ id: "doing", label: "Doing" },
{ id: "done", label: "Done" },
]}
defaultItems={[
{ id: "t1", columnId: "todo", title: "Rotate webhook secrets" },
{ id: "t2", columnId: "todo", title: "Draft the design review" },
{ id: "t3", columnId: "doing", title: "SSO rollout" },
{ id: "t4", columnId: "done", title: "Upgrade the CI runners" },
]}
/>

Props

Prop
Description
columns
required
BoardColumn[]
The columns (lanes), left to right.
items
BoardItem[]
CONTROLLED list; order within a column is array order filtered by `columnId`. The kit never mutates it: apply each `onMove` yourself (`applyBoardMove` is the standard reducer). Omit it (optionally passing `defaultItems`) for uncontrolled use.
defaultItems
BoardItem[]
Initial list for UNCONTROLLED use; Board then applies each move internally.
onItemsChange
(items: BoardItem[]) => void
Fired with the would-be next list after a drop, in both modes.
onMove
(move: BoardMove) => void
Fired on every completed drop with the computed move (source, target, index, neighbors).
onPressItem
(item: BoardItem) => void
Makes each card body pressable and reports the pressed item.
onSelectItemMenu
(item: BoardItem, menuItem: RowMenuItem, menuIndex: number) => void
Fired when a row of a card's kebab menu (`item.menu`) is selected.
columnWidth
number
Column width in px (default 300).
compact
boolean
Density axis (pass none for the default density).
emptyLabel
string
Message shown in an empty column (default "No items").
testID
string
E2E hook forwarded to the root element.
style
StyleProp<ViewStyle>
Outer layout composition only (width/flex), never a restyle hook.

Do & Don’t

Moves stay in the consumer's data

Don’t
To do
1
Rotate webhook secrets
Doing
1
SSO rollout
Don't pass a controlled items and drop the move on the floor: the drag completes, announces, and then the card snaps back because nothing updated the array.
Do
To do
1
Rotate webhook secrets
Doing
1
SSO rollout
Treat items as the single source of truth: apply every onMove to your own array (applyBoardMove), and persist afterId/beforeId if your backend ranks by neighbors.