Listbox

A custom select whose options stay on screen: single or multi-select, an optional detail line under each option, and a checkmark on the chosen items. Reach for it when the options need rich rows; prefer Select for a simple short list.
The list fills the parent it is given; a step of its own (xs, lg, …, with start to pin it to the leading edge) or a Container step sets its measure.
One job, a platform's own control: iOS marks a choice in a list with a trailing check, so there every chosen row carries a check at its end (in single and multi select alike) and no row is filled for being chosen. The web and Android lead the row with the mark: a violet checkmark with the chosen label in the same violet in single-select, the platform's selection checkbox in multi-select (the Material 3 box on Android).
The rows are Dark Factory's menu rows on every platform: bold labels at an 8px corner that take the hover wash at once on the web, a muted second line, and a bordered list in Dark Factory's panel.
Single-select exposes a list of selectable options. With multi, it exposes a checkbox group: each row owns its label, checked state, and tap target. Give either mode a meaningful accessibilityLabel; the fallback is "Options". Arrow keys and Home/End move focus within one tab stop. Single-select follows focus, while multi-select keeps the current selection until Enter or Space toggles the focused row.
Backend
Frontend
Design
iOS
✓
Backend
Frontend
Design
Android
✓
Backend
Frontend
Design
Web
tsx
<Listbox
accessibilityLabel="Teams"
items={[
{ label: "Backend", selected: true },
{ label: "Frontend" },
{ label: "Design" }
]}
/>

Props

Prop
Description
items
required
ListboxItem[]
The options to render, top to bottom.
accessibilityLabel
string
Accessible name of the option list or multi-select checkbox group. Defaults to "Options".
multi
boolean
Multi-select: each row is a checkbox (a leading indicator on the web and Android, a trailing ✓ on iOS) instead of a single choice.
bordered
boolean
Wrap the list in a rounded, bordered content card with static glass in glass mode.
small
boolean
Tighter rows with smaller text.
large
boolean
Taller rows.
disabled
boolean
Dim the list and block selection.
selected
number | number[]
Selected option index(es) (CONTROLLED): a single index in single-select, an array of indices in multi-select. Omit for uncontrolled use.
defaultSelected
number | number[]
Initial selection for uncontrolled use (a bare listbox selects on press). Falls back to the `selected` flags on `items` when omitted.
onChange
(selected: number | number[]) => void
Fired with the full new selection (an index in single-select, an index array in multi).
onSelect
(index: number) => void
Fired with the pressed option's index.
testID
string
E2E hook forwarded to the root list view.
style
LayoutStyle
Composition within a parent only, never a restyle hook and never a width: the parent layout container provides the bounds.
xxxs
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
xxs
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
xs
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
sm
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
md
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
lg
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
xl
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
xxl
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
xxxl
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
wide
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
wider
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
widest
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
page
boolean
Measure (pick one; omit to take the parent's bounds). Caps the component at a step of the width scale, centered in its column; `start` pins it to the leading edge.
start
boolean
Pin a capped component to the leading edge instead of centering it in its column.

Do & Don’t

Prefer a native select for simple lists

Don’t
✓
Yes
No
A custom listbox for two short options is heavier than it needs to be and worse on mobile.
Do
For short, plain lists a native select is lighter, accessible, and uses the platform picker on mobile.

single

Don’t
✓
Backend
✓
Frontend
Design
Platform
Single-select with two checkmarks lies about state: only one option can be the value.
Do
✓
Backend
Frontend
Design
Platform
Show exactly one checkmark, mirror it in the trigger value, and close the panel on pick.

multi

Don’t
Backend
▾
Don't close on each pick or echo only the last choice: multi-select needs to keep all selections visible.
Do
Keep the panel open, toggle each option's own checkmark, and summarize the count in the trigger.