TreeView

A hierarchical tree view component for displaying nested data structures with support for search, selection, and custom actions.

Basic Usage

The TreeView component displays hierarchical data in a collapsible tree structure. Data is provided as a flat array with parent-child relationships defined by parentId.

src
components
TreeView.tsx
Button.tsx
hooks
useTreeState.ts
package.json
import {File, Folder} from "lucide-react";
import {TreeView, useTreeState} from "@vuer-ai/vuer-uikit";
import {useState} from "react";

function FileExplorer() {
  const [hoveredId, setHoveredId] = useState(null);

  // Flat data structure with parentId references
  const fileSystemData = [
    {id: "1", parentId: null, label: "src", etype: "folder", isCollapsible: true},
    {id: "2", parentId: "1", label: "components", etype: "folder", isCollapsible: true},
    {id: "3", parentId: "2", label: "TreeView.tsx", etype: "file"},
    {id: "4", parentId: "2", label: "Button.tsx", etype: "file"},
    {id: "5", parentId: "1", label: "hooks", etype: "folder", isCollapsible: true},
    {id: "6", parentId: "5", label: "useTreeState.ts", etype: "file"},
    {id: "7", parentId: null, label: "package.json", etype: "file"},
  ];
  // Use the tree state hook
  const {visibleData, expandedItems, toggleItem, hasDescendants} = useTreeState({
    data: fileSystemData,
    defaultExpanded: true,
  });
  const getFileIcon = (item) => {
    if (item.etype === "folder") {
    return <Folder className="size-4 shrink-0" />;
  }
    return <File className="size-4 shrink-0" />;
  };

  return (
    <TreeView
      data={visibleData}
      getIcon={getFileIcon}
      className="h-[400px]"
      hoveredId={hoveredId}
      onItemHover={setHoveredId}
      expandedItems={expandedItems}
      onToggleItem={toggleItem}
      hasDescendants={hasDescendants}
    />
  );
}

Add search functionality using the TreeSearchBar component and useTreeSearch hook. The search supports regular expressions and case sensitivity options.

src
components
TreeView.tsx
Button.tsx
hooks
useTreeState.ts
useSearch.ts
utils
helpers.ts
package.json
README.md
function SearchableFileExplorer() {
  const [searchQuery, setSearchQuery] = useState("");
  const [isCaseSensitive, setIsCaseSensitive] = useState(false);
  const [isRegex, setIsRegex] = useState(false);


  const fileSystemData = [/* ... same as above */];


  // Filter data based on search
  const {filteredData, searchResultsCount, isRegexValid} = useTreeSearch({
    data: fileSystemData,
    searchQuery,
    isCaseSensitive,
    isRegex,
  });


  // Process filtered data for display
  const {visibleData, expandedItems, toggleItem, hasDescendants} = useTreeState({
    data: filteredData,
    defaultExpanded: true,
  });


  return (
    <div className="h-[500px] flex flex-col">
      <div className="p-2">
        <TreeSearchBar
          searchQuery={searchQuery}
          setSearchQuery={setSearchQuery}
          isCaseSensitive={isCaseSensitive}
          setIsCaseSensitive={setIsCaseSensitive}
          isRegex={isRegex}
          setIsRegex={setIsRegex}
          isRegexValid={isRegexValid}
          searchResultsCount={searchResultsCount}
        />
      </div>
      <TreeView
        data={visibleData}
        getIcon={getFileIcon}
        className="flex-1"
        expandedItems={expandedItems}
        onToggleItem={toggleItem}
        hasDescendants={hasDescendants}
      />
    </div>
  );
}

Selectable Items

Enable item selection by setting isSelectable={true}. Use selectedItemId and onSelectChange to control the selected state.

Selected: None
src
components
TreeView.tsx
Button.tsx
hooks
useTreeState.ts
useSearch.ts
utils
helpers.ts
package.json
README.md
function SelectableFileExplorer() {

  const [selectedId, setSelectedId] = useState(null);

  return (
    <div>
      <div className="mb-2 text-sm">
        Selected: {selectedId ? fileSystemData.find(item => item.id === selectedId)?.label : "None"}
      </div>
      <TreeView
        data={visibleData}
        getIcon={getFileIcon}
        isSelectable={true}
        selectedItemId={selectedId}
        onSelectChange={setSelectedId}
        expandedItems={expandedItems}
        onToggleItem={toggleItem}
        hasDescendants={hasDescendants}
      />
    </div>
  );
}

With Actions

Add custom action buttons to tree items using the actions property. Actions can be always visible or appear on hover.

src
components
TreeView.tsx
Button.tsx
Input.tsx
hooks
useTreeState.ts
package.json

function FileExplorerWithActions() {
  const [hoveredId, setHoveredId] = useState(null);


  const fileSystemData = [
    {
      id: "1",
      parentId: null,
      label: "src",
      etype: "folder",
      isCollapsible: true,
      actions: (
        <div className="flex gap-1">
          <Plus className="size-3"/>
          <MoreHorizontal className="size-3"/>
        </div>,
      ),
    }, {
      id: "2",
      parentId: "1",
      label: "TreeView.tsx",
      etype: "file",
      actions: (
        <div className="flex gap-1">
          <Edit className="size-3"/>
          <Copy className="size-3"/>
          <Trash2 className="size-3"/>
        </div>
      ),
    },
  ];


  return (
    <TreeView
      data={visibleData}
      getIcon={getFileIcon}
      hoveredId={hoveredId}
      onItemHover={setHoveredId}
      expandedItems={expandedItems}
      onToggleItem={toggleItem}
      hasDescendants={hasDescendants}
    />
  );
}

Disabled Items

Items can be disabled by setting disable: true. Disabled items have muted colors and limited interaction.

src
components
TreeView.tsx
Button.tsx
Input.tsx
hooks
useTreeState.ts
package.json
const fileSystemData = [
  {
    id: "1",
    label: "src",
    etype: "folder",
    isCollapsible: true,
  }, {
    id: "2",
    label: "deprecated-utils.ts",
    etype: "file",
    disable: true, // Disabled file
  }, {
    id: "3",
    label: "legacy",
    etype: "folder",
    isCollapsible: true,
    disable: true, // Disabled but still collapsible
  },
];

The TreeSearchBar can be used separately for custom layouts and styling.

src
components
TreeView.tsx
Button.tsx
hooks
useTreeState.ts
useSearch.ts
utils
helpers.ts
package.json
README.md

function ExternalSearchExample() {
  const [searchQuery, setSearchQuery] = useState("");
  const [isCaseSensitive, setIsCaseSensitive] = useState(false);
  const [isRegex, setIsRegex] = useState(false);


  return (
    <div className="flex flex-col">
      {/* Custom positioned search bar */}
      <div className="bg-gray-50 dark:bg-gray-900 p-2">
        <TreeSearchBar
          searchQuery={searchQuery}
          setSearchQuery={setSearchQuery}
          isCaseSensitive={isCaseSensitive}
          setIsCaseSensitive={setIsCaseSensitive}
          isRegex={isRegex}
          setIsRegex={setIsRegex}
          isRegexValid={true}
          searchResultsCount={0}
        />
      </div>

      {/* Tree without internal search */}
      <TreeView
        data={visibleData}
        getIcon={getFileIcon}
        expandedItems={expandedItems}
        onToggleItem={toggleItem}
        hasDescendants={hasDescendants}
      />
    </div>
  );
}

Hide Expand Button

Control the visibility of expand/collapse buttons with the hideExpand prop. When included, the expand button is hidden until you hover over the item. By default (without this prop), the expand button is always visible.

src
package.json
README.md
<TreeView
  data={visibleData}
  getIcon={getFileIcon}
  hideExpand // Hide expand button until hover
  expandedItems={expandedItems}
  onToggleItem={toggleItem}
  hasDescendants={hasDescendants}
/>

Data Structure

The TreeView uses a flat data structure with parent-child relationships:

type TreeDataItem = {
  id: string;                  // Unique identifier
  parentId: string | null;     // Parent's id (null for root)
  label: string;               // Display text
  isCollapsible?: boolean;     // Can expand/collapse
  actions?: ReactNode;         // Custom actions
  disable?: boolean;           // Disabled state
  [key: string]: any;         // Additional properties
}

API Reference

TreeView Props

PropTypeDefaultDescription
dataTreeDataItem[]-Processed data from useTreeState
getIcon(item) => ReactNode-Icon renderer function
classNamestring-Additional CSS classes
isSelectablebooleanfalseEnable selection
selectedItemIdstring | null-Selected item id
onSelectChange(id) => void-Selection change handler
hoveredIdstring | null-Hovered item id
onItemHover(id) => void-Hover change handler
hideExpandbooleanfalseHide expand buttons
expandedItemsSet<string>-Expanded item ids
onToggleItem(id) => void-Toggle item handler
hasDescendants(id) => boolean-Check for children

TreeSearchBar Props

PropTypeDescription
searchQuerystringCurrent search text
setSearchQuery(query) => voidUpdate search text
isCaseSensitivebooleanCase sensitive search
setIsCaseSensitive(value) => voidToggle case sensitivity
isRegexbooleanUse regex search
setIsRegex(value) => voidToggle regex mode
isRegexValidbooleanIs regex valid
searchResultsCountnumberNumber of results

Hooks

useTreeState

Processes flat data into a tree structure for rendering.

const {visibleData, expandedItems, toggleItem, hasDescendants, expandAll, collapseAll} = useTreeState({
  data: TreeDataItem[],
  defaultExpanded?: boolean
});

useTreeSearch

Filters tree data based on search criteria.

const {filteredData, searchResultsCount, isRegexValid, renderLabel} = useTreeSearch({
  data: TreeDataItem[],
  searchQuery: string,
  isCaseSensitive?: boolean,
  isRegex?: boolean
});