/// Inputs
Cascader
Field that opens cascading columns to drill down a hierarchy of nested options, returning the full path of chosen values.
import { Cascader } from "@protocore/pds";Basics
Each column reveals the children of the option chosen in the column before it. Selecting a leaf commits the whole path and closes the overlay. The value is a string[] of option values; controllable via value / defaultValue / onValueChange.
Change on select
Set changeOnSelect to commit at every level, not just leaves — useful when an intermediate node (a whole region, a whole category) is itself a valid answer.
When to use it
Use Cascader for a strict path through a deep, uniform hierarchy — country → region → city, or category → subcategory → product. The column layout keeps each level's options short. When the hierarchy is browsed as a whole (expanding several branches at once) use TreeSelect; for a flat list use Select.
Usage
Do
- Give the trigger an accessible name via a `Field` label or `aria-label`.
- Keep each level's option set short enough to scan in a column.
- Use `changeOnSelect` when an intermediate node is a valid final answer.
- Read the ordered `path` (and resolved option chain) from `onValueChange`.
Don't
- Don't use it for a flat list — a Select is simpler and faster.
- Don't nest so deep that the columns overflow the viewport.
- Don't leave the trigger unlabelled when no visible field label wraps it.
- Don't reconstruct the path from the trigger text — read the `string[]`.
Accessibility
| Keys | Action |
|---|---|
| Enter / Space | Open the overlay, or select the focused option. |
| Arrow Up / Down | Move within the current column. |
| Arrow Right | Descend into the focused option's children. |
| Arrow Left | Return to the parent column. |
| Esc | Close the overlay without changing the value. |
- The overlay is a Radix Popover: portalled, dismissed on outside-click and Escape, and returns focus to the trigger on close.
- Each column is a `role="listbox"` of `role="option"` buttons with `aria-selected` on the active option; a roving tabindex keeps one option tabbable.
- `invalid` sets `aria-invalid` on the trigger; the trigger exposes `aria-expanded` for its open state.
Cascader props
| Prop | Type | Default | Description |
|---|---|---|---|
changeOnSelect | boolean | false | Also commit when a branch (not just a leaf) is selected. |
className | string | — | |
defaultOpen | boolean | — | Initial open state in uncontrolled mode. |
defaultValue | string[] | — | Initial selected path in uncontrolled mode. |
disabled | boolean | — | Disable the whole control. |
invalid | boolean | — | Mark the field invalid — danger border plus `aria-invalid`. |
onOpenChange | ((open: boolean) => void) | — | Fires when the overlay opens or closes. |
onValueChange | ((path: string[], options: CascaderOption[]) => void) | — | Fires with the next path and the matching option chain. |
open | boolean | — | Controlled open state of the overlay. |
options * | CascaderOption[] | — | The option forest. |
placeholder | string | Select | Text shown when nothing is selected. |
separator | string | / | Separator between labels in the trigger. |
size | enum | md | Field height: `sm` (32) · `md` (36, default) · `lg` (40). |
style | CSSProperties | — | |
value | string[] | — | Controlled selected path (values from root to leaf). |