Skip to content
Data Display

Tree View

A tree of rows you can open, close and walk with the keyboard.

Walkthrough

1. Basic

A file tree. → opens a folder and steps in, ← closes it and steps out.

src
lib
index.ts 12 KB
utils.ts 3 KB
app.css 28 KB
package.json 2 KB
<script lang="ts">	import { TreeView, type TreeNode } from 'omaris';​	const files: TreeNode[] = [		{			id: 'src',			label: 'src',			children: [				{					id: 'lib',					label: 'lib',					children: [						{ id: 'index', label: 'index.ts', meta: '12 KB' },						{ id: 'utils', label: 'utils.ts', meta: '3 KB' }					]				},				{ id: 'app', label: 'app.css', meta: '28 KB' }			]		},		{ id: 'pkg', label: 'package.json', meta: '2 KB' }	];​	let expanded = $state(['src', 'lib']);	let selected = $state<string | null>('utils');</script>​<div class="w-72 max-w-full rounded-shape-md border border-border p-2">	<TreeView items={files} bind:expanded bind:selected label="Files" /></div>

2. Checkable

Ticking a branch ticks its leaves, and a partly ticked branch shows a dash. checked holds only the leaf ids, so there is no half state to store.

Food
Grills
Stews
Drinks
Coffee
Tea

grills, coffee

<script lang="ts">	import { TreeView, type TreeNode } from 'omaris';​	const categories: TreeNode[] = [		{			id: 'food',			label: 'Food',			children: [				{ id: 'grills', label: 'Grills' },				{ id: 'stews', label: 'Stews' }			]		},		{			id: 'drinks',			label: 'Drinks',			children: [				{ id: 'coffee', label: 'Coffee' },				{ id: 'tea', label: 'Tea' }			]		}	];​	let checked = $state(['grills', 'coffee']);</script>​<div class="flex w-72 max-w-full flex-col gap-3">	<TreeView items={categories} checkable bind:checked defaultExpanded label="Categories" />	<p class="text-xs text-muted-foreground">{checked.join(', ') || 'nothing ticked'}</p></div>

3. Lazy

expandable draws the chevron before the children exist. onexpand is where they get fetched; loading swaps the chevron for a spinner meanwhile.

<script lang="ts">	import { TreeView, type TreeNode } from 'omaris';​	let items = $state<TreeNode[]>([		{ id: 'remote', label: 'remote', expandable: true },		{ id: 'local', label: 'local', children: [{ id: 'note', label: 'note.md' }] }	]);	let expanded = $state<string[]>([]);​	function load(node: TreeNode) {		if (node.id !== 'remote' || node.children) return;		items = items.map((entry) => (entry.id === 'remote' ? { ...entry, loading: true } : entry));		setTimeout(() => {			items = items.map((entry) =>				entry.id === 'remote'					? {							...entry,							loading: false,							children: [								{ id: 'origin', label: 'origin.json' },								{ id: 'upstream', label: 'upstream.json' }							]						}					: entry			);		}, 700);	}</script>​<div class="w-72 max-w-full">	<TreeView {items} bind:expanded onexpand={load} label="Sources" /></div>

4. Overridden

A wider indent, pill rows, an accent guide and a row body of your own.

Design open
Tokens
Motion
Code open
API
<script lang="ts">	import { TreeView, type TreeNode } from 'omaris';​	const items: TreeNode[] = [		{			id: 'design',			label: 'Design',			children: [				{ id: 'tokens', label: 'Tokens' },				{ id: 'motion', label: 'Motion' }			]		},		{ id: 'code', label: 'Code', children: [{ id: 'api', label: 'API' }] }	];</script>​<div class="w-72 max-w-full">	<TreeView		{items}		defaultExpanded		label="Sections"		class="[--tree-indent:2rem]"		classes={{ item: 'rounded-full', guide: 'bg-primary/30' }}	>		{#snippet row(node, { expanded })}			<span class="min-w-0 flex-1 truncate font-medium">{node.label}</span>			{#if expanded}				<span class="text-xs text-muted-foreground">open</span>			{/if}		{/snippet}	</TreeView></div>