Board

Organism
A data-driven kanban board: columns scroll horizontally, each column is a drop zone in a sunken well headed by its label and item count, and every card carries a drag grip, an optional trailing badge, a two-line description, a free-form chips slot, and an optional kebab menu.

Playground

Every control sets a real prop of BoardProps. The JSX below updates as you go.
To do
2
Rotate webhook secrets
Draft the design review
Doing
1
SSO rollout
Done
1
Upgrade the CI runners
Props
compact
boolean
draggable
boolean
JSX · BoardProps
<Board draggable />

Variants

6 examples from src/organisms/board/board.md, each rendered live from its own JSX.
Ready
1
Preserve the Argus experience
Review
0
No work here
Plain work lanes
<Board plain emptyLabel="No work here" columns={[{ id: "ready", label: "Ready" }, { id: "review", label: "Review" }]} items={[{ id: "core-1", columnId: "ready", title: "Preserve the Argus experience" }]} />
Ready
1
Preserve the Argus experience
In progress
0
No items
Done
0
No items
Draggable plain work lanes
The grip supports pointer dragging and Space/arrow/Enter keyboard moves. Drop targets fill each column's height, including the space below an empty placeholder. Controlled consumers persist onMove before replacing their saved items.
<Board plain draggable
columns={[{ id: "ready", label: "Ready" }, { id: "doing", label: "In progress" }, { id: "done", label: "Done" }]}
defaultItems={[{ id: "core-1", columnId: "ready", title: "Preserve the Argus experience" }]}
renderItem={(item, { dragHandle }) => (
<Card compact>
<Row between alignCenter><Typography bold>{item.title}</Typography>{dragHandle}</Row>
</Card>
)}
/>
To do
2
Rotate webhook secrets
Draft the design review
Doing
1
SSO rollout
Compact
The density axis tightens the lane padding, the card gaps, and the card insets for a board that has to show more at once.
<Board
compact
columns={[
{ id: "todo", label: "To do" },
{ id: "doing", label: "Doing" },
]}
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" },
]}
/>
To do
1
Doing
1
Card menus and press
onPressItem makes each card's body pressable (the grip and the kebab stay separate controls), and a card with menu rows gains a kebab reported through onSelectItemMenu. chips is a free slot under the title for chips or avatars.
<Board
columns={[
{ id: "todo", label: "To do" },
{ id: "doing", label: "Doing" },
]}
defaultItems={[
{ id: "t1", columnId: "todo", title: "Rotate webhook secrets", menu: [{ label: "Edit", icon: "pencil" }, { label: "Delete", icon: "trash", destructive: true }] },
{ id: "t2", columnId: "doing", title: "SSO rollout", chips: <Chip>identity</Chip> },
]}
onPressItem={(item) => {}}
onSelectItemMenu={(item, menuItem) => {}}
/>
To do
WIP 2
Rotate webhook secrets
Draft the design review
Doing
0
Drop tasks here
Empty column and explicit badges
An empty column shows emptyLabel in a dashed slot and stays a valid drop target. A column's badge prop replaces the automatic item count (a WIP limit, a status).
<Board
columns={[
{ id: "todo", label: "To do", badge: "WIP 2" },
{ id: "doing", label: "Doing" },
]}
defaultItems={[
{ id: "t1", columnId: "todo", title: "Rotate webhook secrets" },
{ id: "t2", columnId: "todo", title: "Draft the design review" },
]}
emptyLabel="Drop tasks here"
/>
To do
1
Rotate webhook secrets
Doing
1
SSO rollout
Controlled
items keeps the list in your hands: each drop reports a BoardMove through onMove, and applyBoardMove is the standard reducer to apply it.
<Stateful initial={[
{ id: "t1", columnId: "todo", title: "Rotate webhook secrets" },
{ id: "t2", columnId: "doing", title: "SSO rollout" },
]}>
{(items, setItems) => (
<Board
columns={[
{ id: "todo", label: "To do" },
{ id: "doing", label: "Doing" },
]}
items={items}
onMove={(move) => setItems(applyBoardMove(items, move))}
/>
)}
</Stateful>

Anatomy

To do
2
Rotate webhook secrets
Draft the design review
Doing
1
SSO rollout
Done
1
Upgrade the CI runners
1
Board
2
Column
3
Card
4
Count
Dimmed parts appear only in some states or variants.

Props

18 props on BoardProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
14 props
plain
boolean
false
Unframed work lanes with custom domain cards.
pageScroll
boolean
false
Let the surrounding page own horizontal overflow instead of an inner scroller.
draggable
boolean
false
Enable drag handles and drop targets in plain lanes.
disabled
boolean
false
Temporarily prevent moves, for example while the consumer saves a drop.
renderItem
(item: BoardItem, controls: { dragHandle: ReactNode }) => ReactNode
–
Custom plain card; place the supplied dragHandle alongside its domain actions.
bleed
number
–
Extend the scroller through its parent's horizontal inset.
columns *
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
300
Column width in px (default 300).
Density axis
4 props
compact
boolean
false
emptyLabel
string
"No items"
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.

Accessibility and keyboard

Keyboard
Space
Lifts or drops the focused card
Arrow keys
Moves a lifted card
Escape
Cancels the move
Semantics
Roles
button
From the docs
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

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
Ready
1
Preserve the Argus experience
Review
0
No work here
Web
control 10 · field 10 · card 16 · Instrument Sans
Ready
1
Preserve the Argus experience
Review
0
No work here
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
Ready
1
Preserve the Argus experience
Review
0
No work here
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

<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" },
]}
/>
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.
For an action-driven work board, plain drops the lane wells and keeps the lane headers and dashed empty slots. renderItem supplies the domain card with its own controls. Add draggable to enable drag grips and drop targets while retaining the plain layout. A custom renderItem receives { dragHandle } as its second argument; place that supplied grip in the card's header. disabled prevents moves while a consumer saves a change. bleed extends horizontal scrolling through the owning container's inset. Plain lanes share the Board's measured width equally, with a 168px minimum per lane. Narrow containers scroll horizontally; long card text stays within its lane.

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--bg2
--fd
--fm
--ft
--ion-ring
--ln
--sf
--sh-lg
--sh-sm
--tx3

Related components