Cascader

Only leaves are selected by default. Set changeOnSelect to allow intermediate levels.

Basics

Drill through columns to select a city, or search the full path.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" placeholder="Choose a city" data={data} searchable /> </Block> ); }

Flat Paths

withColumns={false} lists full paths in one column.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} withColumns={false} /> </Block> ); }

Variants

Compare the default, filled, outline, and unstyled field shells on Cascader.

Loading demo…

import { Column, Cascader } from '@plocks/ui'; const options = [{ value: 'us', label: 'United States', children: [{ value: 'nyc', label: 'New York' }] }]; const variants = ['default', 'filled', 'outline', 'unstyled'] as const; export function Demo() { return ( <Column gap="md" fullWidth> {variants.map(variant => ( <Cascader key={variant} variant={variant} label={`${variant} variant`} data={options} placeholder="Choose a city" /> ))} </Column> ); }

Select Intermediate Levels

changeOnSelect allows a parent path to be selected.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} changeOnSelect /> </Block> ); }

Hover Expansion

expandTrigger="hover" opens the next column on hover. safeAreaPolygon keeps that column open during diagonal pointer travel.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} expandTrigger="hover" /> </Block> ); }

Maximum Displayed Levels

maxDisplayedLevels limits the simultaneous columns.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} maxDisplayedLevels={2} defaultValue={['europe', 'france', 'paris']} /> </Block> ); }

Search Paths

Search switches to a list of matching full paths.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} searchable /> </Block> ); }

Nothing Found

Search displays a fallback when there are no matches.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} searchable nothingFoundMessage="No matching locations" /> </Block> ); }

Formatted Value

formatValue customizes the selected path shown in the trigger.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} defaultValue={['europe', 'france', 'paris']} formatValue={(path) => path.map((item) => item.label).join(' → ')} /> </Block> ); }

Column Width

columnWidth changes the width of each level.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} columnWidth={220} /> </Block> ); }

Clearable

clearable adds a control to remove the selected path.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} defaultValue={['europe', 'france', 'paris']} clearable /> </Block> ); }

Disabled Options

Disabled options stay visible but cannot be selected.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', disabled: true }, { value: 'germany', label: 'Germany' }, ], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} /> </Block> ); }

Custom Option

renderOption customizes each column entry.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} renderOption={(option) => `${option.label ?? option.value} →`} /> </Block> ); }

Read Only

A read-only field displays its path without opening.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} readOnly defaultValue={['europe', 'france', 'paris']} /> </Block> ); }

Error State

error links validation feedback to the field.

Loading demo…

import { Block, Cascader } from '@plocks/ui'; const data = [ { value: 'europe', label: 'Europe', children: [ { value: 'france', label: 'France', children: [ { value: 'paris', label: 'Paris' }, { value: 'lyon', label: 'Lyon' }, ], }, ], }, { value: 'asia', label: 'Asia', children: [{ value: 'japan', label: 'Japan', children: [{ value: 'tokyo', label: 'Tokyo' }] }], }, ]; export function Demo() { return ( <Block fullWidth> <Cascader label="Location" data={data} error="Choose a location" /> </Block> ); }
Scan to open this page on your phoneplocks.dev/ui/Cascader