Tuimorphic

TreeView

Hierarchical file/folder structure with box-drawing characters and keyboard navigation.

Import

import { TreeView } from 'tuimorphic';

Basic Usage

src
index.ts
components
Button.tsx
Input.tsx
package.json
tsconfig.json
const data = [
  {
    id: 'src',
    label: 'src',
    children: [
      { id: 'index.ts', label: 'index.ts', isFile: true },
      {
        id: 'components',
        label: 'components',
        children: [
          { id: 'Button.tsx', label: 'Button.tsx', isFile: true },
        ],
      },
    ],
  },
];
 
<TreeView data={data} defaultExpanded={['src']} />

Props

PropTypeDefaultDescription
dataTreeNode[]-Tree data structure to render (required)
defaultExpandedstring[][]Node IDs that should be expanded by default
expandedstring[]-Controlled expanded state
onSelect(node: TreeNode) => void-Callback when a node is selected
onToggle(nodeId: string, expanded: boolean) => void-Callback when a node is expanded/collapsed
classNamestring-Additional CSS class names

TreeNode

PropTypeDescription
idstringUnique identifier for the node
labelstringDisplay label for the node
childrenTreeNode[]Child nodes (makes this a folder/branch)
isFilebooleanWhether this is a file (leaf node)
iconReact.ReactNodeCustom icon to display

Keyboard Navigation

KeyAction
Arrow Up/DownNavigate between nodes
Arrow RightExpand folder or move to first child
Arrow LeftCollapse folder or move to parent
Enter/SpaceSelect node and toggle expansion

Variants

File Tree

src
index.ts
components
Button.tsx
Input.tsx
package.json
tsconfig.json
const fileTree = [
  {
    id: 'src',
    label: 'src',
    children: [
      { id: 'index.ts', label: 'index.ts', isFile: true },
      {
        id: 'components',
        label: 'components',
        children: [
          { id: 'Button.tsx', label: 'Button.tsx', isFile: true },
          { id: 'Card.tsx', label: 'Card.tsx', isFile: true },
        ],
      },
    ],
  },
  { id: 'package.json', label: 'package.json', isFile: true },
];
 
<TreeView data={fileTree} defaultExpanded={['src', 'components']} />

With Selection Callback

Documents
report.pdf
notes.txt
<TreeView
  data={fileTree}
  onSelect={(node) => console.log('Selected:', node.label)}
  onToggle={(id, expanded) => console.log(id, expanded)}
/>

Controlled Expansion

const [expanded, setExpanded] = useState(['src']);
 
<TreeView
  data={fileTree}
  expanded={expanded}
  onToggle={(nodeId, isExpanded) => {
    if (isExpanded) {
      setExpanded([...expanded, nodeId]);
    } else {
      setExpanded(expanded.filter(id => id !== nodeId));
    }
  }}
/>

Deep Nesting

Level 1
Level 2
Level 3
Level 4
deeply-nested.txt
sibling.ts
const deepTree = [
  {
    id: 'level-1',
    label: 'Level 1',
    children: [
      {
        id: 'level-2',
        label: 'Level 2',
        children: [
          {
            id: 'level-3',
            label: 'Level 3',
            children: [
              { id: 'file', label: 'deep-file.txt', isFile: true },
            ],
          },
        ],
      },
    ],
  },
];
 
<TreeView data={deepTree} defaultExpanded={['level-1', 'level-2', 'level-3']} />

Custom Icons

const treeWithIcons = [
  {
    id: 'folder',
    label: 'Documents',
    icon: '[]',
    children: [
      { id: 'file1', label: 'readme.txt', isFile: true, icon: '#' },
      { id: 'file2', label: 'notes.md', isFile: true, icon: '*' },
    ],
  },
];
 
<TreeView data={treeWithIcons} />

On this page