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

Root

PropDefaultType
collection
TreeCollection<T>

The collection of cascade select nodes

allowParentSelection
boolean

Whether parent (branch) items can be selectable

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
closeOnSelecttrue
boolean

Whether the cascade-select should close when an item is selected

defaultHighlightedValue
string[]

The initial highlighted value of the cascade-select when rendered.

defaultOpen
boolean

The initial open state of the cascade-select when rendered. Use when you don't need to control the open state.

defaultValue
string[][]

The initial value of the cascade-select when rendered. Use when you don't need to control the value.

disabled
boolean

Whether the cascade-select is disabled

form
string

The form attribute of the underlying input element

formatValue
(selectedItems: T[][]) => string

Function to format the display value

highlightedValue
string[]

The controlled highlighted value of the cascade-select

highlightTrigger'click'
'click' | 'hover'

What triggers highlighting of items

id
string

The unique identifier of the machine.

ids
Partial<{ root: string label: string control: string trigger: string indicator: string clearTrigger: string positioner: string content: string hiddenInput: string list(valuePath: string): string item(valuePath: string): string }>

The ids of the cascade-select elements. Useful for composition.

immediate
boolean

Whether to synchronize the present change immediately or defer it to the next frame

invalid
boolean

Whether the cascade-select is invalid

lazyMountfalse
boolean

Whether to enable lazy mounting

loopFocusfalse
boolean

Whether the cascade-select should loop focus when navigating with keyboard

multiplefalse
boolean

Whether to allow multiple selections

name
string

The name attribute of the underlying input element

onExitComplete
VoidFunction

Function called when the animation ends in the closed state

onFocusOutside
(event: FocusOutsideEvent) => void

Function called when the focus is moved outside the component

onHighlightChange
(details: HighlightChangeDetails<T>) => void

Called when the highlighted value changes

onInteractOutside
(event: InteractOutsideEvent) => void

Function called when an interaction happens outside the component

onOpenChange
(details: OpenChangeDetails) => void

Called when the open state changes

onPointerDownOutside
(event: PointerDownOutsideEvent) => void

Function called when the pointer is pressed down outside the component

onValueChange
(details: ValueChangeDetails<T>) => void

Called when the value changes

open
boolean

The controlled open state of the cascade-select

positioning
PositioningOptions

The positioning options for the cascade-select content

present
boolean

Whether the node is present (controlled by the user)

readOnly
boolean

Whether the cascade-select is read-only

required
boolean

Whether the cascade-select is required

scrollToIndexFn
(details: ScrollToIndexDetails) => void

Function to scroll to a specific index in a list

skipAnimationOnMountfalse
boolean

Whether to allow the initial presence animation.

unmountOnExitfalse
boolean

Whether to unmount on exit.

value
string[][]

The controlled value of the cascade-select

AttributeDescription
[data-scope]cascade-select
[data-part]root
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid
[data-state]"open" | "closed"

ClearTrigger

Renders a <button> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]clear-trigger
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid

Content

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]content
[data-activedescendant]The id the active descendant of the content
[data-state]"open" | "closed"
CSS VariableDescription
--layer-indexThe index of the dismissable in the layer stack
--nested-layer-countThe number of nested cascade-selects

Control

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]control
[data-disabled]Present when disabled
[data-focused]
[data-readonly]Present when read-only
[data-invalid]Present when invalid
[data-state]"open" | "closed"

HiddenInput

Renders a <input> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Indicator

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]indicator
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid

ItemIndicator

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]item-indicator
[data-value]The value of the item
[data-highlighted]Present when highlighted
[data-type]The type of the item
[data-state]"checked" | "unchecked"

Item

Renders a <div> element.

PropDefaultType
indexPath
IndexPath

The index path of the item

value
string[]

The value path of the item

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
item
any

The item to render

AttributeDescription
[data-scope]cascade-select
[data-part]item
[data-value]The value of the item
[data-disabled]Present when disabled
[data-highlighted]Present when highlighted
[data-selected]Present when selected
[data-depth]The depth of the item
[data-state]"checked" | "unchecked"
[data-type]The type of the item
[data-index-path]

ItemText

Renders a <span> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]item-text
[data-value]The value of the item
[data-highlighted]Present when highlighted
[data-state]"checked" | "unchecked"
[data-disabled]Present when disabled

Label

Renders a <label> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]label
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid

List

Renders a <div> element.

PropDefaultType
indexPath
IndexPath

The index path of the item

value
string[]

The value path of the item

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
item
any

The item to render

AttributeDescription
[data-scope]cascade-select
[data-part]list
[data-depth]The depth of the item

Positioner

Renders a <div> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
CSS VariableDescription
--reference-widthThe width of the reference element
--reference-heightThe height of the root
--available-widthThe available width in viewport
--available-heightThe available height in viewport
--xThe x position for transform
--yThe y position for transform
--z-indexThe z-index value
--transform-originThe transform origin for animations

RootProvider

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
immediate
boolean

Whether to synchronize the present change immediately or defer it to the next frame

lazyMountfalse
boolean

Whether to enable lazy mounting

onExitComplete
VoidFunction

Function called when the animation ends in the closed state

present
boolean

Whether the node is present (controlled by the user)

skipAnimationOnMountfalse
boolean

Whether to allow the initial presence animation.

unmountOnExitfalse
boolean

Whether to unmount on exit.

value
any

Trigger

Renders a <button> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
AttributeDescription
[data-scope]cascade-select
[data-part]trigger
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-invalid]Present when invalid
[data-focused]
[data-placement]The placement of the trigger

ValueText

Renders a <span> element.

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
placeholder
string

Text to display when no value is selected.

AttributeDescription
[data-scope]cascade-select
[data-part]value-text
[data-disabled]Present when disabled
[data-invalid]Present when invalid
[data-focused]Present when focused

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.