Hierarchical Data Selection for shadcn/ui – Cascader-Shadcn

Select hierarchical data with Cascader-Shadcn's cascading dropdown component. Works with Shadcn UI primitives and Tailwind CSS styling.

Cascader-Shadcn is a cascading dropdown component for handling hierarchical data selection in your React, shadcn/ui, and TailwindCSS projects.

You can use it to build interfaces where users need to navigate through nested categories, locations, organizational structures, or any data that follows a parent-child relationship pattern.

The component supports both desktop and mobile interactions. On desktop, it uses a popover for precise selection. On mobile devices, it switches to a drawer interface to improve usability on smaller screens.

Features

๐Ÿ”„ Hierarchical Navigation: Cascading columns expand dynamically as you traverse nested data structures.

๐Ÿ–ฑ๏ธ Dual Expansion Modes: Switch between click and hover triggers.

โ™ฟ Keyboard Interactions: Arrow keys, Enter, Escape, Tab, and Backspace all function as expected for accessibility compliance.

๐Ÿ“ฑ Mobile-friendly: Automatically switches from popover to drawer layout on mobile viewports.

๐ŸŽจ Custom Rendering: Replace default label displays with custom React components or formatted strings.

๐ŸŽฏ Clear Selection: Built-in clear button removes selected values in one action.

Use Cases

  • Location Selectors: Build a country, state, and city selector where each level filters the next set of options.
  • Product Categories: Navigate through category hierarchies in e-commerce applications where products exist in nested taxonomies.
  • Organization Charts: Select departments, teams, and individual members through structured organizational data.
  • File System Navigation: Browse through folder structures where directories contain subdirectories and files.

How to Use It

1. Install the cascader component through shadcn/ui.

npx shadcn@latest add https://cascader-shadcn.surge.sh/r/cascader.json

2. If you prefer manual control, create a new file named cascader.tsx inside your components/ui folder. Copy the source code from the repository into this file.

3. Install the necessary dependencies:

npm install lucide-react

4. Import the component and define your data structure. The options prop requires an array of objects where each object contains a value, a label, and an optional children array.

The onChange callback returns two arguments: an array of selected value strings and an array of the corresponding full CascaderOption objects.

import React, { useState } from "react";
import { Cascader } from "@/components/ui/cascader";
const categoryData = [
  {
    value: "electronics",
    label: "Electronics",
    children: [
      {
        value: "computers",
        label: "Computers",
        children: [
          { value: "laptops", label: "Laptops" },
          { value: "desktops", label: "Desktops" },
        ],
      },
      {
        value: "phones",
        label: "Mobile Phones",
      },
    ],
  },
  {
    value: "furniture",
    label: "Furniture",
    children: [
      { value: "tables", label: "Tables" },
      { value: "chairs", label: "Chairs" },
    ],
  },
];
export function CategorySelector() {
  const [selected, setSelected] = useState([]);
  const handleChange = (value, selectedOptions) => {
    setSelected(value);
    console.log("Current Selection:", value);
  };
  return (
    <div className="p-4 w-full max-w-sm">
      <label className="block text-sm font-medium mb-2">
        Choose a Category
      </label>
      <Cascader
        options={categoryData}
        value={selected}
        onChange={handleChange}
        placeholder="Select category..."
        expandTrigger="hover"
        className="w-full"
      />
      <div className="mt-4 text-sm text-gray-600">
        Selected Path: {selected.join(" / ")}
      </div>
    </div>
  );
}

5. You can customize how the selected value appears in the input box using the displayRender prop.

<Cascader
  options={categoryData}
  displayRender={(labels) => labels.join(" > ")}
  placeholder="Custom separator..."
/>

API Reference

Cascader Props

PropTypeDefaultDescription
optionsCascaderOption[]โ€”The hierarchical data array to render.
valuestring[]โ€”The controlled value of the selected path.
defaultValuestring[]โ€”The initial selected path.
onChangefunctionโ€”Callback function triggered on selection.
placeholderstring"Please select"Text displayed when the input is empty.
disabledbooleanfalseDisables interaction with the component.
allowClearbooleantrueShows a button to clear the current selection.
expandTrigger"click" | "hover""click"Defines the action that opens submenus.
displayRenderfunctionโ€”Customizes the display format of selected labels.
classNamestringโ€”CSS class for the trigger element.
popupClassNamestringโ€”CSS class for the dropdown content.

CascaderOption Interface

PropertyTypeDescription
valuestringUnique identifier for the option.
labelReactNodeThe content to display for the option.
textLabelstringPlain text fallback for the label.
disabledbooleanPrevents selection of this specific option.
childrenCascaderOption[]Array of nested options.

Related Resources

  • Shadcn UI: A collection of re-usable components built with Radix UI and Tailwind CSS.
  • Lucide React: A consistent and clean icon library used within the component.
  • Tailwind CSS: A utility-first CSS framework for rapid UI development.

FAQs

Q: Can I load child options asynchronously when a parent is selected?
A: The current implementation expects all data upfront in the options prop. You can modify the component to fetch children on demand by updating the options array in your onChange handler and passing the new data back through the value prop.

Q: How do I restrict selection to only leaf nodes?
A: You can only complete a selection by choosing an option without children. Options with children only expand to the next level.

Q: Does keyboard navigation work across all columns?
A: Yes. Arrow keys move within a column, right arrow or Enter expands into child columns, left arrow or Backspace returns to parent columns, and Tab cycles between columns. Escape closes the entire menu.

Q: Can I use this with React Hook Form or other form libraries?
A: Yes. Use the controlled pattern with value and onChange props. Connect these to your form library’s field registration system like you would with any controlled input component.

Q: How do I customize the column width or maximum height?
A: Override the default classes through popupClassName. Target the column divs with CSS to adjust their min-width and max-height properties.

Adem Kouki

Adem Kouki

SOFTWARE ENGINEER ยท FULL-STACK WEB DEVELOPER BY DAY, PENTESTER BY NIGHT

Leave a Reply

Your email address will not be published. Required fields are marked *