Cascader
A nested-path select: drill through columns of options and commit the path you land on. The standard control for category → subcategory → item pickers.
Clicking a branch opens the next column; clicking a leaf commits the whole path and closes the popup. Reopening resumes on the committed path.
Import
import { Cascader } from "@mikenotthepope/substrateui"Search and clear
showSearch filters against the whole joined path, so typing europe por finds Porto. clearable adds a reset button once something is selected.
Selecting any level
By default only leaves are selectable. changeOnSelect commits every branch you pass through too — useful when “Asia” is a valid answer on its own.
Controlled
Pass value with onChange to own the state. The second argument gives you the options along the path, so you have the labels without re-walking the tree.
value: [asia, jp, tokyo]
Accessibility
Each column is a listbox named after the option that opened it, and every option reports its selected state. Branches carry aria-expanded and aria-haspopup so screen readers announce that more choices follow. Keyboard users move within a column with ↑/↓ (plus Home and End), open a branch with the forward arrow, and step back to the parent with the reverse arrow — the arrows swap in RTL, and the chevrons mirror with them. Escape closes the popup and returns focus to the trigger. Disabled options can neither be selected nor expanded.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
options | CascaderOption[] | — | The option tree to choose a path through. |
value | string[] | — | Selected path, root first — controlled. |
defaultValue | string[] | [] | Selected path on mount — uncontrolled. |
onChange | (value: string[], options: CascaderOption[]) => void | — | Fired with the new path and the options along it. |
placeholder | string | "Select..." | Trigger text when nothing is selected. |
separator | string | " / " | Joins labels in the trigger and in search results. |
changeOnSelect | boolean | false | Let branches be selected too, not just leaves. |
showSearch | boolean | false | Add a filter box that searches whole paths. |
clearable | boolean | false | Show a button that resets the selection. |
displayRender | (labels, options) => ReactNode | — | Replace the trigger's label rendering. |
open | boolean | — | Controlled popup open state. |
defaultOpen | boolean | false | Popup open state on mount. |
onOpenChange | (open: boolean) => void | — | Fired when the popup opens or closes. |
labels | CascaderLabels | — | Text overrides: searchPlaceholder, empty, clear, level. |
contentClassName | string | — | Extra classes for the popup. |
CascaderOption
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Unique among its siblings; the path is the list of values from root to node. |
label | React.ReactNode | — | Rendered option content. |
children | CascaderOption[] | — | Child options — presence makes this a branch rather than a leaf. |
disabled | boolean | — | Dim the option and block selecting it or drilling into it. |