Claude
Skills
Sign in
Back

tree-view

Included with Lifetime
$97 forever

MUI X Tree View — SimpleTreeView, RichTreeView, lazy loading, custom icons, multiselect, drag-and-drop, and virtualization

General

What this skill does


# MUI X Tree View

## Package Tiers

| Package | Import | Features |
|---------|--------|---------|
| `@mui/x-tree-view` | `SimpleTreeView`, `TreeItem` | Expansion, selection, keyboard nav, icons (free, MIT) |
| `@mui/x-tree-view-pro` | `RichTreeViewPro` | Drag-and-drop reordering, virtualization |
| `@mui/x-tree-view` | `RichTreeView` | Data-driven tree from items array (free, MIT) |

Always import `TreeItem` from `@mui/x-tree-view/TreeItem` or `@mui/x-tree-view`.

---

## 1. SimpleTreeView — Basic Usage

`SimpleTreeView` uses declarative JSX children. Each `TreeItem` has a required `itemId` and `label`.

```tsx
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';

function BasicTree() {
  return (
    <SimpleTreeView>
      <TreeItem itemId="documents" label="Documents">
        <TreeItem itemId="resume" label="Resume.pdf" />
        <TreeItem itemId="cover-letter" label="CoverLetter.docx" />
      </TreeItem>
      <TreeItem itemId="photos" label="Photos">
        <TreeItem itemId="vacation" label="Vacation">
          <TreeItem itemId="beach" label="beach.jpg" />
        </TreeItem>
      </TreeItem>
    </SimpleTreeView>
  );
}
```

### Controlled Expansion and Selection

```tsx
import { useState } from 'react';
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';

function ControlledTree() {
  const [expandedItems, setExpandedItems] = useState<string[]>(['documents']);
  const [selectedItems, setSelectedItems] = useState<string | null>('resume');

  return (
    <SimpleTreeView
      expandedItems={expandedItems}
      onExpandedItemsChange={(_event, itemIds) => setExpandedItems(itemIds)}
      selectedItems={selectedItems}
      onSelectedItemsChange={(_event, itemId) => setSelectedItems(itemId)}
    >
      <TreeItem itemId="documents" label="Documents">
        <TreeItem itemId="resume" label="Resume.pdf" />
        <TreeItem itemId="notes" label="Notes.txt" />
      </TreeItem>
      <TreeItem itemId="downloads" label="Downloads">
        <TreeItem itemId="installer" label="installer.exe" />
      </TreeItem>
    </SimpleTreeView>
  );
}
```

---

## 2. RichTreeView — Data-Driven

`RichTreeView` renders from an `items` array. Each item implements `TreeViewBaseItem`.

```tsx
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import type { TreeViewBaseItem } from '@mui/x-tree-view/models';

const ITEMS: TreeViewBaseItem[] = [
  {
    id: 'src',
    label: 'src',
    children: [
      {
        id: 'components',
        label: 'components',
        children: [
          { id: 'app', label: 'App.tsx' },
          { id: 'header', label: 'Header.tsx' },
        ],
      },
      { id: 'index', label: 'index.ts' },
    ],
  },
  {
    id: 'config',
    label: 'config',
    children: [
      { id: 'tsconfig', label: 'tsconfig.json' },
      { id: 'package', label: 'package.json' },
    ],
  },
];

function FileExplorer() {
  return (
    <RichTreeView
      items={ITEMS}
      defaultExpandedItems={['src', 'components']}
      sx={{ maxHeight: 400, overflowY: 'auto' }}
    />
  );
}
```

### Custom Item Type with Extra Fields

```tsx
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import type { TreeViewBaseItem } from '@mui/x-tree-view/models';

interface FileItem {
  id: string;
  label: string;
  fileType?: 'folder' | 'file';
  size?: number;
  children?: FileItem[];
}

const items: TreeViewBaseItem<FileItem>[] = [
  {
    id: '1',
    label: 'Documents',
    fileType: 'folder',
    children: [
      { id: '2', label: 'Report.pdf', fileType: 'file', size: 2048 },
      { id: '3', label: 'Notes.md', fileType: 'file', size: 512 },
    ],
  },
];

function TypedTree() {
  return <RichTreeView<FileItem> items={items} />;
}
```

---

## 3. Lazy Loading — Async Data Source

Fetch children on demand when a node is expanded. Use the `experimentalFeatures` and `dataSource` props (available in recent MUI X versions).

### Manual Lazy Loading Pattern

```tsx
import { useState, useCallback } from 'react';
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';
import CircularProgress from '@mui/material/CircularProgress';

interface TreeNode {
  id: string;
  label: string;
  hasChildren: boolean;
}

async function fetchChildren(parentId: string): Promise<TreeNode[]> {
  const res = await fetch(`/api/tree/${parentId}/children`);
  return res.json();
}

function LazyTree() {
  const [nodes, setNodes] = useState<Map<string, TreeNode[]>>(
    new Map([['root', [{ id: '1', label: 'Projects', hasChildren: true }]]])
  );
  const [loading, setLoading] = useState<Set<string>>(new Set());

  const handleExpand = useCallback(
    async (_event: React.SyntheticEvent, itemIds: string[]) => {
      // Find newly expanded items that haven't been loaded
      for (const itemId of itemIds) {
        if (!nodes.has(itemId) && !loading.has(itemId)) {
          setLoading((prev) => new Set(prev).add(itemId));
          const children = await fetchChildren(itemId);
          setNodes((prev) => new Map(prev).set(itemId, children));
          setLoading((prev) => {
            const next = new Set(prev);
            next.delete(itemId);
            return next;
          });
        }
      }
    },
    [nodes, loading]
  );

  const renderNode = (node: TreeNode) => (
    <TreeItem
      key={node.id}
      itemId={node.id}
      label={
        loading.has(node.id) ? (
          <>{node.label} <CircularProgress size={14} /></>
        ) : (
          node.label
        )
      }
    >
      {node.hasChildren &&
        (nodes.get(node.id) ?? [{ id: `${node.id}-placeholder`, label: '' }] as TreeNode[])
          .map(renderNode)}
    </TreeItem>
  );

  return (
    <SimpleTreeView onExpandedItemsChange={handleExpand}>
      {(nodes.get('root') ?? []).map(renderNode)}
    </SimpleTreeView>
  );
}
```

### RichTreeView with Async Data Source (MUI X v7+)

```tsx
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import type { TreeViewBaseItem } from '@mui/x-tree-view/models';

const fetchData = async (parentId: string | null): Promise<TreeViewBaseItem[]> => {
  const res = await fetch(`/api/tree?parent=${parentId ?? 'root'}`);
  return res.json();
};

function AsyncRichTree() {
  return (
    <RichTreeView
      items={[]}
      experimentalFeatures={{ lazyLoading: true }}
      dataSource={{
        getChildrenCount: (item) => item.childrenCount ?? 0,
        getChildren: async (itemId) => fetchData(itemId),
      }}
    />
  );
}
```

---

## 4. Custom Icons

Override expand/collapse/end icons globally via `slots` or per-item via `TreeItem` props.

```tsx
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';
import FolderIcon from '@mui/icons-material/Folder';
import FolderOpenIcon from '@mui/icons-material/FolderOpen';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

function CustomIconTree() {
  return (
    <SimpleTreeView
      slots={{
        expandIcon: ChevronRightIcon,
        collapseIcon: ExpandMoreIcon,
        endIcon: InsertDriveFileIcon,
      }}
    >
      <TreeItem
        itemId="folder1"
        label="Source Code"
        slots={{
          icon: FolderIcon,
          expandIcon: FolderIcon,
          collapseIcon: FolderOpenIcon,
        }}
      >
        <TreeItem itemId="file1" label="index.ts" />
        <TreeItem itemId="file2" label="utils.ts" />
      </TreeItem>
      <TreeItem
        itemId="folder2"
        label="Tests"
        slots={{
          expandIcon: FolderIcon,
          collapseIcon: FolderOpenIcon,
        }}
      >
        <TreeItem itemId="file3" label="index.test.ts" />
      </TreeItem>

Related in General