Components
Cascade select

Cascade Select

Displays nested options in cascading dropdown panels.

Loading...

Anatomy

<CascadeSelect.Root>
  <CascadeSelect.Label />
  <CascadeSelect.Control>
    <CascadeSelect.Trigger>
      <CascadeSelect.ValueText />
      <CascadeSelect.Indicator />
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger />
  </CascadeSelect.Control>
  <CascadeSelect.Positioner>
    <CascadeSelect.Content>
      <CascadeSelect.List>
        <CascadeSelect.Item>
          <CascadeSelect.ItemText />
          <CascadeSelect.ItemIndicator />
        </CascadeSelect.Item>
      </CascadeSelect.List>
    </CascadeSelect.Content>
  </CascadeSelect.Positioner>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

Examples

Controlled

Use the value and onValueChange props to control the selected value.

Root Provider

An alternative way to control the cascade select is to use the RootProvider component and the useCascadeSelect hook. This gives you access to state and methods from outside the component.

Multiple

Enable multiple selection with the multiple prop. Users can select more than one leaf value.

Hover Trigger

Use highlightTrigger="hover" to highlight items on hover instead of requiring keyboard navigation or click.

Allow Parent Selection

By default, only leaf nodes can be selected. Use allowParentSelection to allow branch nodes to be selected as well.

Events

Use onValueChange, onHighlightChange, and onOpenChange to respond to state changes.

Guides

Building the Tree

The cascade select uses createCascadeCollection to define the hierarchical data. Provide nodeToValue and nodeToString functions along with the rootNode to configure the collection.

const collection = createCascadeCollection({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Electronics',
        value: 'electronics',
        children: [
          { label: 'Phones', value: 'phones' },
          { label: 'Laptops', value: 'laptops' },
        ],
      },
    ],
  },
})

Rendering Panels

The cascade select renders one panel per level of depth. Use a recursive component to render the nested lists. Each panel is determined by which item is currently highlighted — use getItemState with highlightedChild and highlightedIndex to recurse into the next level.

const TreeNode = ({ node, indexPath = [], value = [] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })

  return (
    <>
      <CascadeSelect.List item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon />
            ) : (
              <CascadeSelect.ItemIndicator>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>

      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

Hidden Input

The CascadeSelect.HiddenInput component renders a native <input> element that is visually hidden but present in the DOM, enabling native form submission with the selected value.

<CascadeSelect.Root>
  <CascadeSelect.HiddenInput />
  {/* Other CascadeSelect components */}
</CascadeSelect.Root>

API Reference

Props

Context

API

PropertyType
collection
TreeCollection<V>

The tree collection data

open
boolean

Whether the cascade-select is open

focused
boolean

Whether the cascade-select is focused

multiple
boolean

Whether the cascade-select allows multiple selections

disabled
boolean

Whether the cascade-select is disabled

highlightedValue
string[]

The value of the highlighted item

highlightedItems
V[]

The items along the highlighted path

selectedItems
V[][]

The selected items

hasSelectedItems
boolean

Whether there's a selected option

empty
boolean

Whether the cascade-select value is empty

value
string[][]

The current value of the cascade-select

valueAsString
string

The current value as text

focus
() => void

Function to focus on the select input

reposition
(options?: Partial<PositioningOptions>) => void

Function to set the positioning options of the cascade-select

setOpen
(open: boolean) => void

Function to open the cascade-select

setHighlightValue
(value: string | string[]) => void

Function to set the highlighted value (path or single value to find)

clearHighlightValue
() => void

Function to clear the highlighted value

selectValue
(value: string[]) => void

Function to select a value

setValue
(value: string[][]) => void

Function to set the value

clearValue
(value?: string[]) => void

Function to clear the value

getItemState
(props: ItemProps<V>) => ItemState<V>

Returns the state of a cascade-select item

getValueTextProps
() => T["element"]

Returns the props for the value text element

Accessibility

Keyboard Support

KeyDescription
Space
When focus is on trigger, opens the cascade select and focuses the first item.
When focus is on the content, selects the highlighted item.
Enter
When focus is on trigger, opens the cascade select and focuses the first item.
When focus is on content, selects the highlighted item.
ArrowDown
When focus is on trigger, opens the cascade select.
When focus is on content, moves focus to the next item in the current level.
ArrowUp
When focus is on trigger, opens the cascade select and focuses the last item.
When focus is on content, moves focus to the previous item in the current level.
ArrowRight
When focus is on a branch item, expands the next level and moves focus into it.
ArrowLeft
When focus is on a nested level, collapses it and moves focus back to the parent.
When focus is at the root level, closes the cascade select.
Home
Moves focus to the first item in the current level.
End
Moves focus to the last item in the current level.
Esc
Closes the cascade select and moves focus to trigger.