# Cascade Select

URL: https://ark-ui.com/docs/components/cascade-select
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/cascade-select.mdx

Displays nested options in cascading dropdown panels.

---

<ComponentPreview id="CascadeSelect" />

## Anatomy

<Anatomy id="cascade-select" />

```tsx
<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

**Example: basic**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/cascade-select.module.css'

export const Basic = () => (
  <CascadeSelect.Root className={styles.Root} collection={collection}>
    <CascadeSelect.Label className={styles.Label}>Location</CascadeSelect.Label>
    <CascadeSelect.Control className={styles.Control}>
      <CascadeSelect.Trigger className={styles.Trigger}>
        <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select a location" />
        <CascadeSelect.Indicator className={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content className={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const Basic = () => (
  <CascadeSelect.Root class={styles.Root} collection={collection}>
    <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
    <CascadeSelect.Control class={styles.Control}>
      <CascadeSelect.Trigger class={styles.Trigger}>
        <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
        <CascadeSelect.Indicator class={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content class={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})
</script>

<template>
  <CascadeSelect.Root :class="styles.Root" :collection="collection">
    <CascadeSelect.Label :class="styles.Label">Location</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" placeholder="Select a location" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection } from '@ark-ui/svelte/cascade-select'
  import type { UseCascadeSelectReturn } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Americas',
          value: 'americas',
          children: [
            {
              label: 'United States',
              value: 'us',
              children: [
                { label: 'New York', value: 'new-york' },
                { label: 'California', value: 'california' },
              ],
            },
            {
              label: 'Canada',
              value: 'canada',
              children: [
                { label: 'Ontario', value: 'ontario' },
                { label: 'Quebec', value: 'quebec' },
              ],
            },
          ],
        },
        {
          label: 'Europe',
          value: 'europe',
          children: [
            {
              label: 'France',
              value: 'france',
              children: [
                { label: 'Paris', value: 'paris' },
                { label: 'Lyon', value: 'lyon' },
              ],
            },
            {
              label: 'Germany',
              value: 'germany',
              children: [
                { label: 'Berlin', value: 'berlin' },
                { label: 'Munich', value: 'munich' },
              ],
            },
          ],
        },
      ],
    },
  })
</script>

<CascadeSelect.Root class={styles.Root} {collection}>
  <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        <CascadeSelect.Context>
          {#snippet render(api)}
            {@render renderNode(api, collection.rootNode, [], [])}
          {/snippet}
        </CascadeSelect.Context>
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], value: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} {value}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...value, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...value, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

### Controlled

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

**Example: controlled**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/cascade-select.module.css'

export const Controlled = () => {
  const [value, setValue] = useState<string[][]>([])

  return (
    <CascadeSelect.Root
      className={styles.Root}
      collection={collection}
      value={value}
      onValueChange={(details) => setValue(details.value)}
    >
      <CascadeSelect.Label className={styles.Label}>Location</CascadeSelect.Label>
      <CascadeSelect.Control className={styles.Control}>
        <CascadeSelect.Trigger className={styles.Trigger}>
          <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select a location" />
          <CascadeSelect.Indicator className={styles.Indicator}>
            <ChevronsUpDownIcon />
          </CascadeSelect.Indicator>
        </CascadeSelect.Trigger>
        <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
          <XIcon />
        </CascadeSelect.ClearTrigger>
      </CascadeSelect.Control>
      <Portal>
        <CascadeSelect.Positioner>
          <CascadeSelect.Content className={styles.Content}>
            <TreeNode node={collection.rootNode} />
          </CascadeSelect.Content>
        </CascadeSelect.Positioner>
      </Portal>
      <CascadeSelect.HiddenInput />
    </CascadeSelect.Root>
  )
}

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const Controlled = () => {
  const [value, setValue] = createSignal<string[][]>([])

  return (
    <CascadeSelect.Root
      class={styles.Root}
      collection={collection}
      value={value()}
      onValueChange={(details) => setValue(details.value)}
    >
      <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
      <CascadeSelect.Control class={styles.Control}>
        <CascadeSelect.Trigger class={styles.Trigger}>
          <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
          <CascadeSelect.Indicator class={styles.Indicator}>
            <ChevronsUpDownIcon />
          </CascadeSelect.Indicator>
        </CascadeSelect.Trigger>
        <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
          <XIcon />
        </CascadeSelect.ClearTrigger>
      </CascadeSelect.Control>
      <Portal>
        <CascadeSelect.Positioner>
          <CascadeSelect.Content class={styles.Content}>
            <TreeNode node={collection.rootNode} />
          </CascadeSelect.Content>
        </CascadeSelect.Positioner>
      </Portal>
      <CascadeSelect.HiddenInput />
    </CascadeSelect.Root>
  )
}

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

const value = ref<string[][]>([])
</script>

<template>
  <CascadeSelect.Root :class="styles.Root" :collection="collection" v-model="value">
    <CascadeSelect.Label :class="styles.Label">Location</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" placeholder="Select a location" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection } from '@ark-ui/svelte/cascade-select'
  import type { UseCascadeSelectReturn } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  let value = $state<string[][]>([])

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Americas',
          value: 'americas',
          children: [
            {
              label: 'United States',
              value: 'us',
              children: [
                { label: 'New York', value: 'new-york' },
                { label: 'California', value: 'california' },
              ],
            },
            {
              label: 'Canada',
              value: 'canada',
              children: [
                { label: 'Ontario', value: 'ontario' },
                { label: 'Quebec', value: 'quebec' },
              ],
            },
          ],
        },
        {
          label: 'Europe',
          value: 'europe',
          children: [
            {
              label: 'France',
              value: 'france',
              children: [
                { label: 'Paris', value: 'paris' },
                { label: 'Lyon', value: 'lyon' },
              ],
            },
          ],
        },
      ],
    },
  })
</script>

<CascadeSelect.Root
  class={styles.Root}
  {collection}
  {value}
  onValueChange={(details) => {
    value = details.value
  }}
>
  <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        <CascadeSelect.Context>
          {#snippet render(api)}
            {@render renderNode(api, collection.rootNode, [], [])}
          {/snippet}
        </CascadeSelect.Context>
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], nodeValue: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value: nodeValue })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} value={nodeValue}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...nodeValue, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...nodeValue, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

### 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.

**Example: root-provider**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import {
  CascadeSelect,
  createCascadeCollection,
  useCascadeSelect,
  useCascadeSelectContext,
} from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/cascade-select.module.css'

export const RootProvider = () => {
  const api = useCascadeSelect({ collection })

  return (
    <>
      <output>value: {JSON.stringify(api.value)}</output>

      <CascadeSelect.RootProvider className={styles.Root} value={api}>
        <CascadeSelect.Label className={styles.Label}>Location</CascadeSelect.Label>
        <CascadeSelect.Control className={styles.Control}>
          <CascadeSelect.Trigger className={styles.Trigger}>
            <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select a location" />
            <CascadeSelect.Indicator className={styles.Indicator}>
              <ChevronsUpDownIcon />
            </CascadeSelect.Indicator>
          </CascadeSelect.Trigger>
          <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
            <XIcon />
          </CascadeSelect.ClearTrigger>
        </CascadeSelect.Control>
        <Portal>
          <CascadeSelect.Positioner>
            <CascadeSelect.Content className={styles.Content}>
              <TreeNode node={collection.rootNode} />
            </CascadeSelect.Content>
          </CascadeSelect.Positioner>
        </Portal>
        <CascadeSelect.HiddenInput />
      </CascadeSelect.RootProvider>
    </>
  )
}

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import {
  CascadeSelect,
  createCascadeCollection,
  useCascadeSelect,
  useCascadeSelectContext,
} from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const RootProvider = () => {
  const api = useCascadeSelect({ collection })

  return (
    <>
      <output>value: {JSON.stringify(api().value)}</output>

      <CascadeSelect.RootProvider class={styles.Root} value={api}>
        <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
        <CascadeSelect.Control class={styles.Control}>
          <CascadeSelect.Trigger class={styles.Trigger}>
            <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
            <CascadeSelect.Indicator class={styles.Indicator}>
              <ChevronsUpDownIcon />
            </CascadeSelect.Indicator>
          </CascadeSelect.Trigger>
          <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
            <XIcon />
          </CascadeSelect.ClearTrigger>
        </CascadeSelect.Control>
        <Portal>
          <CascadeSelect.Positioner>
            <CascadeSelect.Content class={styles.Content}>
              <TreeNode node={collection.rootNode} />
            </CascadeSelect.Content>
          </CascadeSelect.Positioner>
        </Portal>
        <CascadeSelect.HiddenInput />
      </CascadeSelect.RootProvider>
    </>
  )
}

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection, useCascadeSelect } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

const cascadeSelect = useCascadeSelect({ collection })
</script>

<template>
  <output>value: {{ JSON.stringify(cascadeSelect.value) }}</output>

  <CascadeSelect.RootProvider :class="styles.Root" :value="cascadeSelect">
    <CascadeSelect.Label :class="styles.Label">Location</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" :placeholder="'Select a location'" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.RootProvider>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection, useCascadeSelect } from '@ark-ui/svelte/cascade-select'
  import type { UseCascadeSelectReturn } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Americas',
          value: 'americas',
          children: [
            {
              label: 'United States',
              value: 'us',
              children: [
                { label: 'New York', value: 'new-york' },
                { label: 'California', value: 'california' },
              ],
            },
          ],
        },
        {
          label: 'Europe',
          value: 'europe',
          children: [
            {
              label: 'France',
              value: 'france',
              children: [
                { label: 'Paris', value: 'paris' },
                { label: 'Lyon', value: 'lyon' },
              ],
            },
          ],
        },
      ],
    },
  })

  const id = $props.id()
  const cascadeSelect = useCascadeSelect({ collection, id })
</script>

<output>value: {JSON.stringify(cascadeSelect().value)}</output>

<CascadeSelect.RootProvider class={styles.Root} value={cascadeSelect}>
  <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        {@render renderNode(cascadeSelect, collection.rootNode, [], [])}
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.RootProvider>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], value: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} {value}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...value, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...value, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

### Multiple

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

**Example: multiple**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/cascade-select.module.css'

export const Multiple = () => (
  <CascadeSelect.Root className={styles.Root} collection={collection} multiple>
    <CascadeSelect.Label className={styles.Label}>Locations</CascadeSelect.Label>
    <CascadeSelect.Control className={styles.Control}>
      <CascadeSelect.Trigger className={styles.Trigger}>
        <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select locations" />
        <CascadeSelect.Indicator className={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content className={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const Multiple = () => (
  <CascadeSelect.Root class={styles.Root} collection={collection} multiple>
    <CascadeSelect.Label class={styles.Label}>Locations</CascadeSelect.Label>
    <CascadeSelect.Control class={styles.Control}>
      <CascadeSelect.Trigger class={styles.Trigger}>
        <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select locations" />
        <CascadeSelect.Indicator class={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content class={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})
</script>

<template>
  <CascadeSelect.Root :class="styles.Root" :collection="collection" multiple>
    <CascadeSelect.Label :class="styles.Label">Locations</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" placeholder="Select locations" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection } from '@ark-ui/svelte/cascade-select'
  import type { UseCascadeSelectReturn } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Americas',
          value: 'americas',
          children: [
            {
              label: 'United States',
              value: 'us',
              children: [
                { label: 'New York', value: 'new-york' },
                { label: 'California', value: 'california' },
              ],
            },
            {
              label: 'Canada',
              value: 'canada',
              children: [
                { label: 'Ontario', value: 'ontario' },
                { label: 'Quebec', value: 'quebec' },
              ],
            },
          ],
        },
        {
          label: 'Europe',
          value: 'europe',
          children: [
            {
              label: 'France',
              value: 'france',
              children: [
                { label: 'Paris', value: 'paris' },
                { label: 'Lyon', value: 'lyon' },
              ],
            },
            {
              label: 'Germany',
              value: 'germany',
              children: [
                { label: 'Berlin', value: 'berlin' },
                { label: 'Munich', value: 'munich' },
              ],
            },
          ],
        },
      ],
    },
  })
</script>

<CascadeSelect.Root class={styles.Root} {collection} multiple>
  <CascadeSelect.Label class={styles.Label}>Locations</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select locations" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        <CascadeSelect.Context>
          {#snippet render(api)}
            {@render renderNode(api, collection.rootNode, [], [])}
          {/snippet}
        </CascadeSelect.Context>
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], value: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} {value}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...value, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...value, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

### Hover Trigger

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

**Example: hover-trigger**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/cascade-select.module.css'

export const HoverTrigger = () => (
  <CascadeSelect.Root className={styles.Root} collection={collection} highlightTrigger="hover">
    <CascadeSelect.Label className={styles.Label}>Location</CascadeSelect.Label>
    <CascadeSelect.Control className={styles.Control}>
      <CascadeSelect.Trigger className={styles.Trigger}>
        <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select a location" />
        <CascadeSelect.Indicator className={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content className={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const HoverTrigger = () => (
  <CascadeSelect.Root class={styles.Root} collection={collection} highlightTrigger="hover">
    <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
    <CascadeSelect.Control class={styles.Control}>
      <CascadeSelect.Trigger class={styles.Trigger}>
        <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
        <CascadeSelect.Indicator class={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content class={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
          {
            label: 'Canada',
            value: 'canada',
            children: [
              { label: 'Ontario', value: 'ontario' },
              { label: 'Quebec', value: 'quebec' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
          {
            label: 'Germany',
            value: 'germany',
            children: [
              { label: 'Berlin', value: 'berlin' },
              { label: 'Munich', value: 'munich' },
            ],
          },
        ],
      },
    ],
  },
})
</script>

<template>
  <CascadeSelect.Root :class="styles.Root" :collection="collection" highlightTrigger="hover">
    <CascadeSelect.Label :class="styles.Label">Location</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" placeholder="Select a location" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection } from '@ark-ui/svelte/cascade-select'
  import type { UseCascadeSelectReturn } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Americas',
          value: 'americas',
          children: [
            {
              label: 'United States',
              value: 'us',
              children: [
                { label: 'New York', value: 'new-york' },
                { label: 'California', value: 'california' },
              ],
            },
            {
              label: 'Canada',
              value: 'canada',
              children: [
                { label: 'Ontario', value: 'ontario' },
                { label: 'Quebec', value: 'quebec' },
              ],
            },
          ],
        },
        {
          label: 'Europe',
          value: 'europe',
          children: [
            {
              label: 'France',
              value: 'france',
              children: [
                { label: 'Paris', value: 'paris' },
                { label: 'Lyon', value: 'lyon' },
              ],
            },
            {
              label: 'Germany',
              value: 'germany',
              children: [
                { label: 'Berlin', value: 'berlin' },
                { label: 'Munich', value: 'munich' },
              ],
            },
          ],
        },
      ],
    },
  })
</script>

<CascadeSelect.Root class={styles.Root} {collection} highlightTrigger="hover">
  <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        <CascadeSelect.Context>
          {#snippet render(api)}
            {@render renderNode(api, collection.rootNode, [], [])}
          {/snippet}
        </CascadeSelect.Context>
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], value: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} {value}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...value, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...value, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

### Allow Parent Selection

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

**Example: allow-parent-selection**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/cascade-select.module.css'

export const AllowParentSelection = () => (
  <CascadeSelect.Root className={styles.Root} collection={collection} allowParentSelection>
    <CascadeSelect.Label className={styles.Label}>Category</CascadeSelect.Label>
    <CascadeSelect.Control className={styles.Control}>
      <CascadeSelect.Trigger className={styles.Trigger}>
        <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select a category" />
        <CascadeSelect.Indicator className={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content className={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Electronics',
        value: 'electronics',
        children: [
          {
            label: 'Phones',
            value: 'phones',
            children: [
              { label: 'Android', value: 'android' },
              { label: 'iPhone', value: 'iphone' },
            ],
          },
          {
            label: 'Laptops',
            value: 'laptops',
            children: [
              { label: 'Gaming', value: 'gaming-laptop' },
              { label: 'Ultrabook', value: 'ultrabook' },
            ],
          },
        ],
      },
      {
        label: 'Clothing',
        value: 'clothing',
        children: [
          { label: 'Shirts', value: 'shirts' },
          { label: 'Pants', value: 'pants' },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const AllowParentSelection = () => (
  <CascadeSelect.Root class={styles.Root} collection={collection} allowParentSelection>
    <CascadeSelect.Label class={styles.Label}>Category</CascadeSelect.Label>
    <CascadeSelect.Control class={styles.Control}>
      <CascadeSelect.Trigger class={styles.Trigger}>
        <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a category" />
        <CascadeSelect.Indicator class={styles.Indicator}>
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Portal>
      <CascadeSelect.Positioner>
        <CascadeSelect.Content class={styles.Content}>
          <TreeNode node={collection.rootNode} />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Portal>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
)

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Electronics',
        value: 'electronics',
        children: [
          {
            label: 'Phones',
            value: 'phones',
            children: [
              { label: 'Android', value: 'android' },
              { label: 'iPhone', value: 'iphone' },
            ],
          },
          {
            label: 'Laptops',
            value: 'laptops',
            children: [
              { label: 'Gaming', value: 'gaming-laptop' },
              { label: 'Ultrabook', value: 'ultrabook' },
            ],
          },
        ],
      },
      {
        label: 'Clothing',
        value: 'clothing',
        children: [
          { label: 'Shirts', value: 'shirts' },
          { label: 'Pants', value: 'pants' },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Electronics',
        value: 'electronics',
        children: [
          {
            label: 'Phones',
            value: 'phones',
            children: [
              { label: 'Android', value: 'android' },
              { label: 'iPhone', value: 'iphone' },
            ],
          },
          {
            label: 'Laptops',
            value: 'laptops',
            children: [
              { label: 'Gaming', value: 'gaming-laptop' },
              { label: 'Ultrabook', value: 'ultrabook' },
            ],
          },
        ],
      },
      {
        label: 'Clothing',
        value: 'clothing',
        children: [
          { label: 'Shirts', value: 'shirts' },
          { label: 'Pants', value: 'pants' },
        ],
      },
    ],
  },
})
</script>

<template>
  <CascadeSelect.Root :class="styles.Root" :collection="collection" allowParentSelection>
    <CascadeSelect.Label :class="styles.Label">Category</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" placeholder="Select a category" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection } from '@ark-ui/svelte/cascade-select'
  import type { UseCascadeSelectReturn } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Electronics',
          value: 'electronics',
          children: [
            {
              label: 'Phones',
              value: 'phones',
              children: [
                { label: 'Android', value: 'android' },
                { label: 'iPhone', value: 'iphone' },
              ],
            },
            {
              label: 'Laptops',
              value: 'laptops',
              children: [
                { label: 'Gaming', value: 'gaming-laptop' },
                { label: 'Ultrabook', value: 'ultrabook' },
              ],
            },
          ],
        },
        {
          label: 'Clothing',
          value: 'clothing',
          children: [
            { label: 'Shirts', value: 'shirts' },
            { label: 'Pants', value: 'pants' },
          ],
        },
      ],
    },
  })
</script>

<CascadeSelect.Root class={styles.Root} {collection} allowParentSelection>
  <CascadeSelect.Label class={styles.Label}>Category</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a category" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        <CascadeSelect.Context>
          {#snippet render(api)}
            {@render renderNode(api, collection.rootNode, [], [])}
          {/snippet}
        </CascadeSelect.Context>
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], value: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} {value}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...value, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...value, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

### Events

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

**Example: events**

#### React

```tsx
import { Portal } from '@ark-ui/react/portal'
import { CascadeSelect, createCascadeCollection, useCascadeSelectContext } from '@ark-ui/react/cascade-select'
import type { CascadeSelectHighlightChangeDetails, CascadeSelectValueChangeDetails } from '@ark-ui/react/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/cascade-select.module.css'

export const Events = () => {
  const handleValueChange = (details: CascadeSelectValueChangeDetails<Node>) => {
    console.log('value changed:', details.value, details.items)
  }

  const handleHighlightChange = (details: CascadeSelectHighlightChangeDetails<Node>) => {
    console.log('highlight changed:', details.highlightedValue, details.highlightedItems)
  }

  const handleOpenChange = (details: { open: boolean }) => {
    console.log('open changed:', details.open)
  }

  return (
    <CascadeSelect.Root
      className={styles.Root}
      collection={collection}
      onValueChange={handleValueChange}
      onHighlightChange={handleHighlightChange}
      onOpenChange={handleOpenChange}
    >
      <CascadeSelect.Label className={styles.Label}>Location</CascadeSelect.Label>
      <CascadeSelect.Control className={styles.Control}>
        <CascadeSelect.Trigger className={styles.Trigger}>
          <CascadeSelect.ValueText className={styles.ValueText} placeholder="Select a location" />
          <CascadeSelect.Indicator className={styles.Indicator}>
            <ChevronsUpDownIcon />
          </CascadeSelect.Indicator>
        </CascadeSelect.Trigger>
        <CascadeSelect.ClearTrigger className={styles.ClearTrigger}>
          <XIcon />
        </CascadeSelect.ClearTrigger>
      </CascadeSelect.Control>
      <Portal>
        <CascadeSelect.Positioner>
          <CascadeSelect.Content className={styles.Content}>
            <TreeNode node={collection.rootNode} />
          </CascadeSelect.Content>
        </CascadeSelect.Positioner>
      </Portal>
      <CascadeSelect.HiddenInput />
    </CascadeSelect.Root>
  )
}

const TreeNode = ({ node, indexPath = [], value = [] }: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const api = useCascadeSelectContext()
  const nodeState = api.getItemState({ item: node, indexPath, value })
  return (
    <>
      <CascadeSelect.List className={styles.List} item={node} indexPath={indexPath} value={value}>
        {collection.getNodeChildren(node).map((child, i) => (
          <CascadeSelect.Item
            className={styles.Item}
            key={collection.getNodeValue(child)}
            item={child}
            indexPath={[...indexPath, i]}
            value={[...value, collection.getNodeValue(child)]}
          >
            <CascadeSelect.ItemText className={styles.ItemText}>
              {collection.stringifyNode(child)}
            </CascadeSelect.ItemText>
            {collection.isBranchNode(child) ? (
              <ChevronRightIcon className={styles.BranchIndicator} />
            ) : (
              <CascadeSelect.ItemIndicator className={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
            )}
          </CascadeSelect.Item>
        ))}
      </CascadeSelect.List>
      {nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild) && (
        <TreeNode
          node={nodeState.highlightedChild as Node}
          indexPath={[...indexPath, nodeState.highlightedIndex]}
          value={[...value, collection.getNodeValue(nodeState.highlightedChild)]}
        />
      )}
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Solid

```tsx
import {
  CascadeSelect,
  createCascadeCollection,
  useCascadeSelectContext,
  type CascadeSelectHighlightChangeDetails,
  type CascadeSelectValueChangeDetails,
} from '@ark-ui/solid/cascade-select'
import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/cascade-select.module.css'

export const Events = () => {
  const handleValueChange = (details: CascadeSelectValueChangeDetails<Node>) => {
    console.log('value changed:', details.value, details.items)
  }

  const handleHighlightChange = (details: CascadeSelectHighlightChangeDetails<Node>) => {
    console.log('highlight changed:', details.highlightedValue, details.highlightedItems)
  }

  const handleOpenChange = (details: { open: boolean }) => {
    console.log('open changed:', details.open)
  }

  return (
    <CascadeSelect.Root
      class={styles.Root}
      collection={collection}
      onValueChange={handleValueChange}
      onHighlightChange={handleHighlightChange}
      onOpenChange={handleOpenChange}
    >
      <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
      <CascadeSelect.Control class={styles.Control}>
        <CascadeSelect.Trigger class={styles.Trigger}>
          <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
          <CascadeSelect.Indicator class={styles.Indicator}>
            <ChevronsUpDownIcon />
          </CascadeSelect.Indicator>
        </CascadeSelect.Trigger>
        <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
          <XIcon />
        </CascadeSelect.ClearTrigger>
      </CascadeSelect.Control>
      <Portal>
        <CascadeSelect.Positioner>
          <CascadeSelect.Content class={styles.Content}>
            <TreeNode node={collection.rootNode} />
          </CascadeSelect.Content>
        </CascadeSelect.Positioner>
      </Portal>
      <CascadeSelect.HiddenInput />
    </CascadeSelect.Root>
  )
}

const TreeNode = (props: { node: Node; indexPath?: number[]; value?: string[] }) => {
  const context = useCascadeSelectContext()
  const nodeState = () =>
    context().getItemState({ item: props.node, indexPath: props.indexPath ?? [], value: props.value ?? [] })
  return (
    <>
      <CascadeSelect.List
        class={styles.List}
        item={props.node}
        indexPath={props.indexPath ?? []}
        value={props.value ?? []}
      >
        <For each={collection.getNodeChildren(props.node)}>
          {(child, getIndex) => (
            <CascadeSelect.Item
              class={styles.Item}
              item={child}
              indexPath={[...(props.indexPath ?? []), getIndex()]}
              value={[...(props.value ?? []), collection.getNodeValue(child)]}
            >
              <CascadeSelect.ItemText class={styles.ItemText}>{collection.stringifyNode(child)}</CascadeSelect.ItemText>
              <Show
                when={collection.isBranchNode(child)}
                fallback={<CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>}
              >
                <ChevronRightIcon class={styles.BranchIndicator} />
              </Show>
            </CascadeSelect.Item>
          )}
        </For>
      </CascadeSelect.List>
      <Show when={nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild as Node)}>
        <TreeNode
          node={nodeState().highlightedChild as Node}
          indexPath={[...(props.indexPath ?? []), nodeState().highlightedIndex]}
          value={[...(props.value ?? []), collection.getNodeValue(nodeState().highlightedChild as Node)]}
        />
      </Show>
    </>
  )
}

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { CascadeSelect, createCascadeCollection } from '@ark-ui/vue/cascade-select'
import type { CascadeSelectHighlightChangeDetails, CascadeSelectValueChangeDetails } from '@ark-ui/vue/cascade-select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/cascade-select.module.css'
import TreeNode from './tree-node.vue'

interface Node {
  label: string
  value: string
  children?: Node[]
}

const collection = createCascadeCollection<Node>({
  nodeToValue: (node) => node.value,
  nodeToString: (node) => node.label,
  rootNode: {
    label: 'Root',
    value: 'root',
    children: [
      {
        label: 'Americas',
        value: 'americas',
        children: [
          {
            label: 'United States',
            value: 'us',
            children: [
              { label: 'New York', value: 'new-york' },
              { label: 'California', value: 'california' },
            ],
          },
        ],
      },
      {
        label: 'Europe',
        value: 'europe',
        children: [
          {
            label: 'France',
            value: 'france',
            children: [
              { label: 'Paris', value: 'paris' },
              { label: 'Lyon', value: 'lyon' },
            ],
          },
        ],
      },
    ],
  },
})

const handleValueChange = (details: CascadeSelectValueChangeDetails<Node>) => {
  console.log('value changed:', details.value, details.items)
}

const handleHighlightChange = (details: CascadeSelectHighlightChangeDetails<Node>) => {
  console.log('highlight changed:', details.highlightedValue, details.highlightedItems)
}

const handleOpenChange = (details: { open: boolean }) => {
  console.log('open changed:', details.open)
}
</script>

<template>
  <CascadeSelect.Root
    :class="styles.Root"
    :collection="collection"
    @value-change="handleValueChange"
    @highlight-change="handleHighlightChange"
    @open-change="handleOpenChange"
  >
    <CascadeSelect.Label :class="styles.Label">Location</CascadeSelect.Label>
    <CascadeSelect.Control :class="styles.Control">
      <CascadeSelect.Trigger :class="styles.Trigger">
        <CascadeSelect.ValueText :class="styles.ValueText" placeholder="Select a location" />
        <CascadeSelect.Indicator :class="styles.Indicator">
          <ChevronsUpDownIcon />
        </CascadeSelect.Indicator>
      </CascadeSelect.Trigger>
      <CascadeSelect.ClearTrigger :class="styles.ClearTrigger">
        <XIcon />
      </CascadeSelect.ClearTrigger>
    </CascadeSelect.Control>
    <Teleport to="body">
      <CascadeSelect.Positioner>
        <CascadeSelect.Content :class="styles.Content">
          <TreeNode :node="collection.rootNode" :collection="collection" />
        </CascadeSelect.Content>
      </CascadeSelect.Positioner>
    </Teleport>
    <CascadeSelect.HiddenInput />
  </CascadeSelect.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Portal } from '@ark-ui/svelte/portal'
  import { CascadeSelect, createCascadeCollection } from '@ark-ui/svelte/cascade-select'
  import type {
    CascadeSelectHighlightChangeDetails,
    CascadeSelectValueChangeDetails,
    UseCascadeSelectReturn,
  } from '@ark-ui/svelte/cascade-select'
  import { ChevronRightIcon, ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
  import styles from 'styles/cascade-select.module.css'

  interface Node {
    label: string
    value: string
    children?: Node[]
  }

  const handleValueChange = (details: CascadeSelectValueChangeDetails<Node>) => {
    console.log('value changed:', details.value, details.items)
  }

  const handleHighlightChange = (details: CascadeSelectHighlightChangeDetails<Node>) => {
    console.log('highlight changed:', details.highlightedValue, details.highlightedItems)
  }

  const handleOpenChange = (details: { open: boolean }) => {
    console.log('open changed:', details.open)
  }

  const collection = createCascadeCollection<Node>({
    nodeToValue: (node) => node.value,
    nodeToString: (node) => node.label,
    rootNode: {
      label: 'Root',
      value: 'root',
      children: [
        {
          label: 'Americas',
          value: 'americas',
          children: [
            {
              label: 'United States',
              value: 'us',
              children: [
                { label: 'New York', value: 'new-york' },
                { label: 'California', value: 'california' },
              ],
            },
          ],
        },
        {
          label: 'Europe',
          value: 'europe',
          children: [
            {
              label: 'France',
              value: 'france',
              children: [
                { label: 'Paris', value: 'paris' },
                { label: 'Lyon', value: 'lyon' },
              ],
            },
          ],
        },
      ],
    },
  })
</script>

<CascadeSelect.Root
  class={styles.Root}
  {collection}
  onValueChange={handleValueChange}
  onHighlightChange={handleHighlightChange}
  onOpenChange={handleOpenChange}
>
  <CascadeSelect.Label class={styles.Label}>Location</CascadeSelect.Label>
  <CascadeSelect.Control class={styles.Control}>
    <CascadeSelect.Trigger class={styles.Trigger}>
      <CascadeSelect.ValueText class={styles.ValueText} placeholder="Select a location" />
      <CascadeSelect.Indicator class={styles.Indicator}>
        <ChevronsUpDownIcon />
      </CascadeSelect.Indicator>
    </CascadeSelect.Trigger>
    <CascadeSelect.ClearTrigger class={styles.ClearTrigger}>
      <XIcon />
    </CascadeSelect.ClearTrigger>
  </CascadeSelect.Control>
  <Portal>
    <CascadeSelect.Positioner>
      <CascadeSelect.Content class={styles.Content}>
        <CascadeSelect.Context>
          {#snippet render(api)}
            {@render renderNode(api, collection.rootNode, [], [])}
          {/snippet}
        </CascadeSelect.Context>
      </CascadeSelect.Content>
    </CascadeSelect.Positioner>
  </Portal>
  <CascadeSelect.HiddenInput />
</CascadeSelect.Root>

{#snippet renderNode(api: UseCascadeSelectReturn, node: Node, indexPath: number[], value: string[])}
  {@const nodeState = api().getItemState({ item: node, indexPath, value })}
  <CascadeSelect.List class={styles.List} item={node} {indexPath} {value}>
    {#each collection.getNodeChildren(node) as child, i}
      <CascadeSelect.Item
        class={styles.Item}
        item={child}
        indexPath={[...indexPath, i]}
        value={[...value, collection.getNodeValue(child)]}
      >
        <CascadeSelect.ItemText class={styles.ItemText}>
          {collection.stringifyNode(child)}
        </CascadeSelect.ItemText>
        {#if collection.isBranchNode(child)}
          <ChevronRightIcon class={styles.BranchIndicator} />
        {:else}
          <CascadeSelect.ItemIndicator class={styles.ItemIndicator}>✓</CascadeSelect.ItemIndicator>
        {/if}
      </CascadeSelect.Item>
    {/each}
  </CascadeSelect.List>
  {#if nodeState.highlightedChild && collection.isBranchNode(nodeState.highlightedChild)}
    {@render renderNode(
      api,
      nodeState.highlightedChild as Node,
      [...indexPath, nodeState.highlightedIndex],
      [...value, collection.getNodeValue(nodeState.highlightedChild as Node)],
    )}
  {/if}
{/snippet}

```

## 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.

```tsx
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.

```tsx
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.

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

## API Reference

### Props

**Component API Reference**

#### React

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The collection of cascade select nodes |
| `allowParentSelection` | `boolean` | No | Whether parent (branch) items can be selectable |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `closeOnSelect` | `boolean` | No | Whether the cascade-select should close when an item is selected |
| `defaultHighlightedValue` | `string[]` | No | The initial highlighted value of the cascade-select when rendered. |
| `defaultOpen` | `boolean` | No | The initial open state of the cascade-select when rendered.
Use when you don't need to control the open state. |
| `defaultValue` | `string[][]` | No | The initial value of the cascade-select when rendered.
Use when you don't need to control the value. |
| `disabled` | `boolean` | No | Whether the cascade-select is disabled |
| `form` | `string` | No | The form attribute of the underlying input element |
| `formatValue` | `(selectedItems: T[][]) => string` | No | Function to format the display value |
| `highlightedValue` | `string[]` | No | The controlled highlighted value of the cascade-select |
| `highlightTrigger` | `'click' | 'hover'` | No | What triggers highlighting of items |
| `id` | `string` | No | 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
}>` | No | The ids of the cascade-select elements. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `invalid` | `boolean` | No | Whether the cascade-select is invalid |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loopFocus` | `boolean` | No | Whether the cascade-select should loop focus when navigating with keyboard |
| `multiple` | `boolean` | No | Whether to allow multiple selections |
| `name` | `string` | No | The name attribute of the underlying input element |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onHighlightChange` | `(details: HighlightChangeDetails<T>) => void` | No | Called when the highlighted value changes |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Called when the open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onValueChange` | `(details: ValueChangeDetails<T>) => void` | No | Called when the value changes |
| `open` | `boolean` | No | The controlled open state of the cascade-select |
| `positioning` | `PositioningOptions` | No | The positioning options for the cascade-select content |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `readOnly` | `boolean` | No | Whether the cascade-select is read-only |
| `required` | `boolean` | No | Whether the cascade-select is required |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails) => void` | No | Function to scroll to a specific index in a list |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
| `value` | `string[][]` | No | The controlled value of the cascade-select |


**Root Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ClearTrigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | content |
| `[data-activedescendant]` | The id the active descendant of the content |
| `[data-state]` | "open" | "closed" |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested cascade-selects |


**Control Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Control Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemIndicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |


**Item Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Label Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | label |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-invalid]` | Present when invalid |


**List Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |


**List Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | list |
| `[data-depth]` | The depth of the item |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--reference-width` | The width of the reference element |
| `--reference-height` | The height of the root |
| `--available-width` | The available width in viewport |
| `--available-height` | The available height in viewport |
| `--x` | The x position for transform |
| `--y` | The y position for transform |
| `--z-index` | The z-index value |
| `--transform-origin` | The transform origin for animations |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
| `value` | `any` | No |  |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `placeholder` | `string` | No | Text to display when no value is selected. |


**ValueText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | value-text |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
| `[data-focused]` | Present when focused |


#### Solid

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The collection of cascade select nodes |
| `allowParentSelection` | `boolean` | No | Whether parent (branch) items can be selectable |
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `closeOnSelect` | `boolean` | No | Whether the cascade-select should close when an item is selected |
| `defaultHighlightedValue` | `string[]` | No | The initial highlighted value of the cascade-select when rendered. |
| `defaultOpen` | `boolean` | No | The initial open state of the cascade-select when rendered.
Use when you don't need to control the open state. |
| `defaultValue` | `string[][]` | No | The initial value of the cascade-select when rendered.
Use when you don't need to control the value. |
| `disabled` | `boolean` | No | Whether the cascade-select is disabled |
| `form` | `string` | No | The form attribute of the underlying input element |
| `formatValue` | `(selectedItems: T[][]) => string` | No | Function to format the display value |
| `highlightedValue` | `string[]` | No | The controlled highlighted value of the cascade-select |
| `highlightTrigger` | `'click' | 'hover'` | No | What triggers highlighting of items |
| `id` | `string` | No | 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
}>` | No | The ids of the cascade-select elements. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `invalid` | `boolean` | No | Whether the cascade-select is invalid |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loopFocus` | `boolean` | No | Whether the cascade-select should loop focus when navigating with keyboard |
| `multiple` | `boolean` | No | Whether to allow multiple selections |
| `name` | `string` | No | The name attribute of the underlying input element |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onHighlightChange` | `(details: HighlightChangeDetails<T>) => void` | No | Called when the highlighted value changes |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Called when the open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onValueChange` | `(details: ValueChangeDetails<T>) => void` | No | Called when the value changes |
| `open` | `boolean` | No | The controlled open state of the cascade-select |
| `positioning` | `PositioningOptions` | No | The positioning options for the cascade-select content |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `readOnly` | `boolean` | No | Whether the cascade-select is read-only |
| `required` | `boolean` | No | Whether the cascade-select is required |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails) => void` | No | Function to scroll to a specific index in a list |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
| `value` | `string[][]` | No | The controlled value of the cascade-select |


**Root Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'button'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ClearTrigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | content |
| `[data-activedescendant]` | The id the active descendant of the content |
| `[data-state]` | "open" | "closed" |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested cascade-selects |


**Control Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Control Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'input'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemIndicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |


**Item Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'label'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Label Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | label |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-invalid]` | Present when invalid |


**List Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |


**List Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | list |
| `[data-depth]` | The depth of the item |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--reference-width` | The width of the reference element |
| `--reference-height` | The height of the root |
| `--available-width` | The available width in viewport |
| `--available-height` | The available height in viewport |
| `--x` | The x position for transform |
| `--y` | The y position for transform |
| `--z-index` | The z-index value |
| `--transform-origin` | The transform origin for animations |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseCascadeSelectReturn<T>` | Yes |  |
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'button'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `placeholder` | `string` | No | Text to display when no value is selected. |


**ValueText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | value-text |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
| `[data-focused]` | Present when focused |


#### Vue

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The tree collection data |
| `allowParentSelection` | `boolean` | No | Whether parent (branch) items can be selectable |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `closeOnSelect` | `boolean` | No | Whether the cascade-select should close when an item is selected |
| `defaultHighlightedValue` | `string[]` | No | The initial highlighted value of the cascade-select when rendered. |
| `defaultOpen` | `boolean` | No | The initial open state of the cascade-select when rendered.
Use when you don't need to control the open state. |
| `defaultValue` | `string[][]` | No | The initial value of the cascade-select when rendered.
Use when you don't need to control the value. |
| `disabled` | `boolean` | No | Whether the cascade-select is disabled |
| `form` | `string` | No | The associate form of the underlying input element |
| `formatValue` | `(selectedItems: T[][]) => string` | No | Function to format the display value |
| `highlightedValue` | `string[]` | No | The controlled highlighted value of the cascade-select |
| `highlightTrigger` | `'click' | 'hover'` | No | What triggers highlighting of items |
| `id` | `string` | No | 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
}>` | No | The ids of the cascade-select elements. Useful for composition. |
| `invalid` | `boolean` | No | Whether the cascade-select is invalid |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loopFocus` | `boolean` | No | Whether the cascade-select should loop focus when navigating with keyboard |
| `modelValue` | `string[][]` | No | The v-model value of the cascade-select |
| `multiple` | `boolean` | No | Whether to allow multiple selections |
| `name` | `string` | No | The name attribute of the underlying input element |
| `open` | `boolean` | No | The controlled open state of the cascade-select |
| `positioning` | `PositioningOptions` | No | The positioning options for the cascade-select content |
| `readOnly` | `boolean` | No | Whether the cascade-select is read-only |
| `required` | `boolean` | No | Whether the cascade-select is required |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails) => void` | No | Function to scroll to a specific index in a list |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Root Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ClearTrigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | content |
| `[data-activedescendant]` | The id the active descendant of the content |
| `[data-state]` | "open" | "closed" |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested cascade-selects |


**Control Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Control Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemIndicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |


**Item Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Label Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | label |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-invalid]` | Present when invalid |


**List Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |


**List Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | list |
| `[data-depth]` | The depth of the item |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--reference-width` | The width of the reference element |
| `--reference-height` | The height of the root |
| `--available-width` | The available width in viewport |
| `--available-height` | The available height in viewport |
| `--x` | The x position for transform |
| `--y` | The y position for transform |
| `--z-index` | The z-index value |
| `--transform-origin` | The transform origin for animations |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
| `value` | `any` | No |  |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `placeholder` | `string` | No |  |


**ValueText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | value-text |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
| `[data-focused]` | Present when focused |


#### Svelte

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The collection of cascade select nodes |
| `allowParentSelection` | `boolean` | No | Whether parent (branch) items can be selectable |
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `closeOnSelect` | `boolean` | No | Whether the cascade-select should close when an item is selected |
| `defaultHighlightedValue` | `string[]` | No | The initial highlighted value of the cascade-select when rendered. |
| `defaultOpen` | `boolean` | No | The initial open state of the cascade-select when rendered.
Use when you don't need to control the open state. |
| `defaultValue` | `string[][]` | No | The initial value of the cascade-select when rendered.
Use when you don't need to control the value. |
| `disabled` | `boolean` | No | Whether the cascade-select is disabled |
| `form` | `string` | No | The form attribute of the underlying input element |
| `formatValue` | `(selectedItems: T[][]) => string` | No | Function to format the display value |
| `highlightedValue` | `string[]` | No | The controlled highlighted value of the cascade-select |
| `highlightTrigger` | `'click' | 'hover'` | No | What triggers highlighting of items |
| `id` | `string` | No | 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
}>` | No | The ids of the cascade-select elements. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `invalid` | `boolean` | No | Whether the cascade-select is invalid |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loopFocus` | `boolean` | No | Whether the cascade-select should loop focus when navigating with keyboard |
| `multiple` | `boolean` | No | Whether to allow multiple selections |
| `name` | `string` | No | The name attribute of the underlying input element |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onHighlightChange` | `(details: HighlightChangeDetails<T>) => void` | No | Called when the highlighted value changes |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Called when the open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onValueChange` | `(details: ValueChangeDetails<T>) => void` | No | Called when the value changes |
| `open` | `boolean` | No | The controlled open state of the cascade-select |
| `positioning` | `PositioningOptions` | No | The positioning options for the cascade-select content |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `readOnly` | `boolean` | No | Whether the cascade-select is read-only |
| `ref` | `Element` | No |  |
| `required` | `boolean` | No | Whether the cascade-select is required |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails) => void` | No | Function to scroll to a specific index in a list |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
| `value` | `string[][]` | No | The controlled value of the cascade-select |


**Root Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**ClearTrigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | content |
| `[data-activedescendant]` | The id the active descendant of the content |
| `[data-state]` | "open" | "closed" |


**Content CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested cascade-selects |


**Context Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `render` | `Snippet<[UseCascadeSelectReturn<any>]>` | Yes |  |


**Control Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Control Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'input'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Indicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Indicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 |


**ItemContext Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `render` | `Snippet<[UseCascadeSelectItemContext]>` | Yes |  |


**ItemIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**ItemIndicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |
| `ref` | `Element` | No |  |


**Item Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**ItemText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'label'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Label Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | label |
| `[data-disabled]` | Present when disabled |
| `[data-readonly]` | Present when read-only |
| `[data-invalid]` | Present when invalid |


**List Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `IndexPath` | Yes | The index path of the item |
| `value` | `string[]` | Yes | The value path of the item |
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |
| `ref` | `Element` | No |  |


**List Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | cascade-select |
| `[data-part]` | list |
| `[data-depth]` | The depth of the item |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Positioner CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--reference-width` | The width of the reference element |
| `--reference-height` | The height of the root |
| `--available-width` | The available width in viewport |
| `--available-height` | The available height in viewport |
| `--x` | The x position for transform |
| `--y` | The y position for transform |
| `--z-index` | The z-index value |
| `--transform-origin` | The transform origin for animations |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseCascadeSelectReturn<any>` | Yes |  |
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `ref` | `Element` | No |  |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `placeholder` | `string` | No |  |
| `ref` | `Element` | No |  |


**ValueText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[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:**

| Property | Type | Description |
|----------|------|-------------|
| `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

<KeyBindingsTable id="cascade-select" />