Tree
Import
Section titled “Import”import { Tree } from '@delightstack/components';import type { TreeNode, FlatTreeNode } from '@delightstack/components';Basic Usage
Section titled “Basic Usage”- package.json
- tsconfig.json
- README.md
View code
<script> import { Tree } from '@delightstack/components';
const data = [ { id: 'src', label: 'src', children: [ { id: 'components', label: 'components', children: [ { id: 'button', label: 'Button.svelte' }, { id: 'modal', label: 'Modal.svelte' }, { id: 'table', label: 'Table.svelte' }, ], }, { id: 'utils', label: 'utils', children: [ { id: 'format', label: 'format.ts' }, { id: 'validate', label: 'validate.ts' }, ], }, { id: 'app', label: 'app.ts' }, { id: 'index', label: 'index.ts' }, ], }, { id: 'package', label: 'package.json' }, { id: 'tsconfig', label: 'tsconfig.json' }, { id: 'readme', label: 'README.md' }, ];
let expanded = $state(['src', 'components']);</script>
<Tree {data} bind:expanded show_lines />Examples
Section titled “Examples”Selectable with Icons
Section titled “Selectable with Icons”Click a file to select it. Folders expand/collapse on click.
View code
<script> import { Tree } from '@delightstack/components';
const fileTree = [ { id: '1', label: 'src', children: [ { id: '1-1', label: 'components', children: [ { id: '1-1-1', label: 'Button.svelte' }, { id: '1-1-2', label: 'Modal.svelte' }, ], }, { id: '1-2', label: 'app.ts' }, ], }, { id: '2', label: 'package.json' }, ];
let selected = $state([]); let expanded = $state(['1']);</script>
<Tree data={fileTree} selectable bind:selected bind:expanded/>Checkbox Selection
Section titled “Checkbox Selection”Parent checkboxes reflect children state with an indeterminate state when partially selected. Checking a parent checks all children.
View code
<script> import { Tree } from '@delightstack/components';
const categories = [ { id: 'fruits', label: 'Fruits', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, { id: 'cherry', label: 'Cherry' }, ], }, { id: 'vegetables', label: 'Vegetables', children: [ { id: 'carrot', label: 'Carrot' }, { id: 'broccoli', label: 'Broccoli' }, { id: 'spinach', label: 'Spinach' }, ], }, ];
let selected = $state([]); let expanded = $state(['fruits', 'vegetables']);</script>
<Tree data={categories} checkboxes multi_select bind:selected bind:expanded />Flat Data with Parent IDs
Section titled “Flat Data with Parent IDs”The component automatically detects flat data (arrays with parentId properties) and builds the tree structure internally.
View code
<Tree data={[ { id: '1', parentId: null, label: 'Root' }, { id: '2', parentId: '1', label: 'Child 1' }, { id: '3', parentId: '1', label: 'Child 2' }, { id: '4', parentId: '2', label: 'Grandchild 1' }, { id: '5', parentId: '2', label: 'Grandchild 2' }, { id: '6', parentId: '3', label: 'Grandchild 3' }, ]} selectable/>Search/Filter
Section titled “Search/Filter”Nodes matching the filter term are highlighted. Non-matching branches without matching descendants are hidden, and parent nodes auto-expand to reveal matches.
- src
- components
- Modal.svelte
- Table.svelte
- utils
- format.ts
- validate.ts
- app.ts
- package.json
- README.md
View code
<script> import { Tree } from '@delightstack/components';
let searchTerm = $state('');</script>
<input bind:value={searchTerm} placeholder="Search..." /><Tree data={fileTree} filter={searchTerm} />Drag-and-Drop
Section titled “Drag-and-Drop”Enable drag-and-drop reordering between nodes.
- README.md
View code
<Tree data={fileTree} draggable ondrop={({ node, target, position }) => { updateTree(node, target, position); }}/>Lazy Loading
Section titled “Lazy Loading”Load children on demand when a node is expanded. A loading spinner replaces the chevron during the fetch.
- Team Alpha
- Team Beta
- Team Gamma
View code
<Tree data={rootNodes} load_children={async (node) => { return await fetchChildren(node.id); }}/>Connecting Lines
Section titled “Connecting Lines”- package.json
View code
<Tree data={fileTree} show_lines bind:expanded />Custom Node Content
Section titled “Custom Node Content”- package.json v2.0.0
- README.md
View code
{#snippet customNode({ node, level })} <span>{node.label}</span> {#if node.data?.badge} <span class="badge">{node.data.badge}</span> {/if}{/snippet}
<Tree data={fileTree} node_content={customNode} />Density
Section titled “Density”Use dense for compact node spacing or comfortable for relaxed spacing. The default sits between the two.
- package.json
- README.md
- package.json
- README.md
View code
<Tree data={fileTree} dense show_lines />
<Tree data={fileTree} comfortable show_lines />Skeleton Loading
Section titled “Skeleton Loading”View code
<Tree skeleton={loading} skeleton_count={7} skeleton_depth={3} data={loading ? [] : data} />| Prop | Type | Default | Description |
|---|---|---|---|
data | TreeNode[] | FlatTreeNode[] | required | Tree data (nested or flat with parent IDs) |
selected | string[] | [] | Selected node IDs (bindable) |
expanded | string[] | [] | Expanded node IDs (bindable) |
selectable | boolean | false | Enable node selection |
multi_select | boolean | false | Allow multiple selection |
checkboxes | boolean | false | Show checkboxes for selection |
show_lines | boolean | false | Show connecting lines between nodes |
draggable | boolean | false | Enable drag-and-drop reordering |
filter | string | - | Search/filter term to highlight and filter matches |
dense | boolean | false | Compact node spacing |
comfortable | boolean | false | Relaxed node spacing |
skeleton | boolean | false | Show loading skeleton |
skeleton_count | number | 5 | Number of skeleton nodes |
skeleton_depth | number | 2 | Nesting depth of skeleton nodes |
id | string | auto | Element ID |
class | string | '' | Additional CSS classes |
load_children | (node: TreeNode) => Promise<TreeNode[]> | - | Lazy load children on expand |
node_content | Snippet<[{ node: TreeNode; level: number }]> | - | Custom node content renderer |
Events
Section titled “Events”| Event | Detail | Description |
|---|---|---|
onselect | { node: TreeNode, selected: string[] } | Selection changed |
onexpand | { node: TreeNode, expanded: boolean } | Node expanded or collapsed |
ondrop | { node: TreeNode, target: TreeNode, position: 'before' | 'after' | 'inside' } | Node dropped after drag |
TreeNode
Section titled “TreeNode”The nested tree data format:
interface TreeNode { id: string; label: string; icon?: Component; children?: TreeNode[]; disabled?: boolean; data?: unknown;}FlatTreeNode
Section titled “FlatTreeNode”The flat data format with parent references:
interface FlatTreeNode { id: string; parentId: string | null; label: string; icon?: Component; disabled?: boolean; data?: unknown;}Keyboard Navigation
Section titled “Keyboard Navigation”| Key | Action |
|---|---|
| Arrow Down | Move focus to next visible node |
| Arrow Up | Move focus to previous visible node |
| Arrow Right | Expand focused node, or move to first child if already expanded |
| Arrow Left | Collapse focused node, or move to parent if already collapsed |
| Enter / Space | Select or toggle checkbox on focused node |
| Home | Move focus to first visible node |
| End | Move focus to last visible node |
| * (asterisk) | Expand all siblings of the focused node |
Accessibility
Section titled “Accessibility”- Container has
role="tree", each node hasrole="treeitem" aria-expandedon expandable nodesaria-selectedon selectable nodesaria-levelindicating nesting deptharia-activedescendantfor focus managementaria-multiselectablewhen multiple selection is enabledaria-disabledon disabled nodes- Full keyboard navigation with arrow keys, Home, End, Enter, Space
- Expand/collapse animations respect
prefers-reduced-motion - Search matches are highlighted with
<mark>elements