Early bird: 30% off Dashral ProSee pricing

Dashral

Tree SelectNew

A field that picks from nested options: province, city and district, or a product's category, shown as its whole path.

$ pnpm dlx shadcn@latest add @dashral/tree-select

Pick the district: couriers price by it.

Installation

$ pnpm dlx shadcn@latest add @dashral/tree-select

Adds the component, the components it builds on, its npm packages and the theme tokens it uses. Or by its full URL: https://dashral.com/r/tree-select.json

When to use

  • Picking one item from a hierarchy in a form: province, city and district for an address, a product's category, a team inside a department.
  • Several items across a hierarchy, with multiple: the areas a courier covers, the categories a discount applies to.

When not to use

  • A flat list of options: Select, or Combobox to search it.
  • A hierarchy people browse and work in, not pick from: Tree View on the page.

Usage

selectable="leaf" lets only the deepest level be picked; the field shows the whole path.

example.tsx
"use client"

import { TreeSelect } from "@/components/dashral/tree-select"

export default function DeliveryArea() {
  return (
    <TreeSelect
      label="Delivery area"
      selectable="leaf"
      items={[
        {
          id: "west-java",
          label: "West Java",
          children: [
            { id: "bandung", label: "Bandung", children: [{ id: "coblong", label: "Coblong" }, { id: "sukajadi", label: "Sukajadi" }] },
            { id: "bogor", label: "Bogor", children: [{ id: "bogor-tengah", label: "Bogor Tengah" }] },
          ],
        },
      ]}
      onValueChange={(district) => console.log(district)}
    />
  )
}

Using the npm package? Import from @dashral/ui/components/tree-select instead, here and in the examples; the rest is the same.

Examples

A product's category

Any level can be picked: a branch's label picks it, its arrow only opens it. Icons on the top level, a disabled item, and picking the current one again clears it.

Any level can be picked: a whole category or one inside it. Picked: audio

Several areas

multiple: a checkbox on every row, a branch ticks all of its items, and a city with every district ticked is one chip.

  • South Jakarta
  • Coblong

4 districts ticked. A city with all of its districts ticked shows as one chip.

API reference

TreeSelect

PropTypeDefault
itemsTreeItem[]

The nested options, as Tree View takes them: id, label, icon, children, disabled. Required.

—
value / defaultValuestring | null

The picked item's id, controlled or initial.

null
onValueChange(value: string | null) => void

The new id, or null when it's cleared.

—
selectable"any" | "leaf"

"any": a branch's label picks it and its arrow opens it. "leaf": only items without children can be picked; a branch only opens.

"any"
multipleboolean

A checkbox on every row. value is then string[] (the ticked items without children), and what's ticked shows as chips.

false
maxShownnumber

With multiple: chips shown before the rest collapse into “+N”.

3
placeholder / searchPlaceholderstring—
emptyTextstring

When the search matches nothing.

"No matches"
searchableboolean

A search box above the tree.

true
defaultExpandedstring[] | "all"

The branches open the first time; the ones leading to the value open every time.

—
namestring

Sends the value with a form: one hidden input, or one per value with multiple.

—
disabled / required / id / aria-label—
labelReactNode

Shown above, linked to the control.

—
descriptionReactNode

A hint under the control.

—
errorboolean | ReactNode

true shows the error style; a message also shows under the control.

—
labelActionReactNode

At the end of the label's row, e.g. a “Forgot password?” link or the current value.

—
withAsteriskboolean

Marks the label with an asterisk, e.g. alongside required.

false
classNames{ root, label, description, error, trigger, content, tree }

Classes for each part.

—
classNamestring

Extra classes, merged with cn() so yours win.

—

Keyboard

KeysAction
EnterorSpaceor↓On the field: open the list
TypeIn the search: narrow the tree, with the branches leading to a match open
↓From the search into the tree
↑ ↓orHomeorEndMove between rows
→or←Open a branch or go into it; close it or go up to its parent
EnterorSpacePick the row (or tick it with multiple); with selectable="leaf", open or close a branch
EscapeClose the list and return to the field
BackspaceWith multiple, on the field: remove the last chip

Accessibility

  • The field is a combobox that opens a tree (the WAI-ARIA tree pattern, from Tree View); the path is read with commas between its steps.
  • Each chip's remove button names what it removes; with multiple, the field says how many are picked.
  • With label, description or error, the id, aria-describedby and aria-invalid are wired up for you.

Motion

  • The list opens as Popover does; branches slide open and closed; chips scale in as they're added.