Shadcn Cascader
Browse 20 production-ready shadcn cascader components for hierarchical selection. Covers drill-down attribute pickers, Miller columns, tree mode, multi-select with cascading branches, per-level and deep search, async levels with paging, virtualized levels, removable chips, custom rows, flat adjacency data, and an embedded panel with no popover.
Shadcn Cascader: Nested Multi-Level Selection
ReUI Cascader is a custom shadcn component for choosing a value out of a tree rather than a flat list. It is built on the same Base UI combobox foundation as the shadcn combobox, so it inherits the ARIA combobox roles, aria-activedescendant list navigation, typeahead and dismiss behavior, then adds level navigation on top.
One mode prop picks how the tree is presented: drill replaces the level as you go deeper, columns lays the open trail out side by side as Miller columns, and tree expands branches in place. The data, the keyboard model, the search and the selection rules are the same in all three.
What is a Shadcn Cascader?
A cascader is a select whose options form a tree. Instead of scrolling one long list you drill from a category into its children and commit a value at the depth that matters, which is what attribute pickers, category trees, org charts, region pickers, permission scopes and file paths need. Pressing a branch opens it instead of committing a selection, the popup stays open, and a breadcrumb and a back control say where you are.
ReUI Cascader is a custom shadcn component rather than part of base shadcn/ui. It wraps Base UI's combobox primitives directly instead of the shadcn combobox wrapper, which clamps the popup to the trigger width, and it reuses the same cn-combobox-* class hooks, so every ReUI style's radius, density and hover treatment applies with no extra CSS. The trigger renders the selection's full path, because in a nested picker a leaf label alone is often ambiguous.
Nested Data, a Parent Pointer, or a Server
Items can be a nested tree where each node carries children, or a flat adjacency list where each row carries a parent id and you pass getParent. Both normalize into the same internal index in a single linear pass, which is what keeps a normalized dataset cheap to render: rows may arrive in any order, an unknown parent makes a node a root rather than dropping it, and duplicate values are ignored after the first occurrence.
Levels can also be fetched instead of supplied. getChildren asks for exactly the levels that are on screen, returns a page with an optional cursor, and hasChildren: true marks a branch before its children exist. Fetched pages are merged on top of items rather than folded into it, so a new items array never discards what the user already drilled into. Past virtualizeThreshold rows a level is windowed by CascaderVirtualItems, per column in columns mode, with row heights measured after mount rather than assumed.
Shadcn Cascader Features
- Drill, Columns and Tree Modes. One mode prop picks the panel layout. Drill replaces the level, columns renders the whole open trail side by side with each pane scrolling on its own, and tree expands branches in place with a leading expander and per-depth indentation.
- Multi-Select with Cascading Branches. multiple turns rows into checkboxes and keeps the popup open, max caps the selection, and cascade selects a whole subtree and reconciles its ancestors, with partly selected branches carrying aria-checked mixed.
- Per-Level and Deep Search. Typing filters the current level. searchScope="deep" searches the level you are on and everything under it, trailing each hit with its ancestor path. Node keywords add extra matchable terms, and filter replaces the matcher outright.
- Async Levels with Paging and Retry. getChildren fetches one level at a time with an AbortSignal and an optional cursor. The Load more row is a real keyboard-reachable option, and a press loads before it navigates, so a failure leaves the reader on the level they were reading.
- Virtualized Levels. CascaderVirtualItems windows a level past virtualizeThreshold rows, and virtualizes per column in columns mode. Row heights are measured after mount rather than assumed, so two-line and deep-search rows do not drift the scroll position.
- Pinned Footer Actions. CascaderFooter pins commands below the list, where they survive an empty filter and a level change. A footer row can open a side flyout that is a real menu with roving focus, typeahead and its own Escape.
- Composable Rows, Trigger and Chips. renderLabel swaps the label block while the count, chevron and check keep working, renderItem replaces the row, CascaderValue formats the selected path, and CascaderChips or useCascaderSelection build a trigger of your own.
- Native Form Submission. name submits through a hidden input like a native field, so a plain form action or any form library works without a controller wrapper. form, id, required, readOnly, invalid and inputRef go with it.
- Full i18n and RTL. Every user-facing string, including aria-labels and live-region announcements, comes from the labels prop, shallow-merged over the English defaults. The panel mirrors under dir rtl, level keys included.
Selection, Cascade, Search and Keyboard
By default only leaves are selectable and branches navigate. Set selectable="any" or pass a predicate to let a category be committed too, in which case the chevron becomes its own target so the branch can still be opened. multiple turns rows into checkboxes, keeps the popup open while picking, and max caps the selection. cascade propagates a commit over the pressed node's loaded subtree and reconciles its ancestors, so a branch with some but not all of its subtree selected renders indeterminate, a state that is derived rather than stored.
Typing filters the current level. With searchScope="deep" it searches the level you are on AND everything under it, not the whole tree, and annotates each result with its ancestor path so identically named leaves in different branches stay distinguishable; tree mode always matches at any depth and auto-expands the ancestors of every hit. onSearch replaces the local scan with a server query, debounced by searchDebounce.
Arrow Right drills into the highlighted branch when the caret is at the end of the query, Arrow Left steps back out when it is at the start, and Backspace steps back when the query is empty, so the level keys never take caret movement away from an edit. Tree mode follows the ARIA tree keys instead. A polite live region announces each level change, each expand and collapse, and each result count, because the breadcrumb that gives sighted users their bearings is invisible to a screen reader.
Integrating With Other Components
Pair the cascader with Shadcn Data Grid as a column editor or a category cell, where a nested value has to be committed in the space of a table cell, and with Shadcn Filters when a faceted filter bar needs one field that scopes by category tree rather than by a flat option list.
Set inline and drop CascaderPanel into the body of a Shadcn Dialog or Shadcn Sheet, where a popover inside a popover would be awkward. In a form, wrap the trigger in a Shadcn Field with a Shadcn Label: name and id make it submit and label like a native input.
Shadcn Button is the natural trigger through the composition prop, Shadcn Badge renders removable chips and per-row status, and every level scrolls inside a Shadcn Scroll Area, which is what gives each Miller column a thumb of its own. Reach for Shadcn Tree when the surface is for browsing rather than committing a value, and Shadcn Combobox when the options are a flat list.
Form Blocks in ReUI Pro
The free examples above pair with 12 production ready form blocks in ReUI Pro, complete sections built on the same primitives and ready to copy into your app.
- Form (12 blocks)Form layouts for settings, profiles, and data entry with validation and grouped fields.