Tree

Data Display

Hierarchical tree view with expand and collapse.

Preview

Usage

example.jsx
import { Tree } from "@/components/ui/tree";

export default function Example() {
  return <Tree />;
}

Source Code

Copy this file into components/ui/tree.jsx in your project.

tree.jsx
"use client";

import { forwardRef, useState } from "react";
import { cn } from "@/lib/utils";

const TreeItem = ({ item, level = 0, defaultOpen = false }) => {
  const [open, setOpen] = useState(defaultOpen);
  const hasChildren = item.children && item.children.length > 0;

  return (
    <div>
      <button
        onClick={() => hasChildren && setOpen(!open)}
        className={cn("flex w-full items-center gap-1 rounded-sm px-2 py-1 text-sm transition-colors hover:bg-accent",
          !hasChildren && "cursor-default"
        )}
        style={{ paddingLeft: `${level * 16 + 8}px` }}
      >
        {hasChildren ? (
          <svg className={cn("h-4 w-4 shrink-0 transition-transform", open && "rotate-90")} fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /></svg>
        ) : (
          <span className="h-4 w-4" />
        )}
        <span className={cn(item.icon && "flex items-center gap-2")}>
          {item.icon && <span>{item.icon}</span>}
          {item.label}
        </span>
      </button>
      {open && hasChildren && (
        <div>{item.children.map((child, i) => <TreeItem key={i} item={child} level={level + 1} />)}</div>
      )}
    </div>
  );
};

const Tree = forwardRef(({ className, items = [], ...props }, ref) => (
  <div ref={ref} className={cn("rounded-md border p-2", className)} {...props}>
    {items.map((item, i) => <TreeItem key={i} item={item} />)}
  </div>
));
Tree.displayName = "Tree";

export { Tree };

Quick Install

Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.

npm install clsx tailwind-merge