Skip to content
bref / ui

Svelte components, simply.

Star

Tree view

Nested items with expandable branches and selectable rows.

Usage

Svelte
<script lang="ts">
	import { TreeView } from 'bref-ui';
	const items = [
		{
			id: 'projects',
			label: 'Projects',
			children: [{ id: 'notes', label: 'Notes' }]
		}
	];
	let selection = $state<string>();
</script>

<TreeView {items} label="Files" bind:selection />

Selection and expansion

Labels share an icon column, and each nesting level advances by the disclosure column. Selected rows use a quiet fill; touch controls receive larger targets.

Live preview

Workspace

Projects and shared files

Personal
Selected: tokens

Settings

Appearance
Content
Interaction
View generated code
Svelte
<script lang="ts">
	import { TreeView } from 'bref-ui';
	import type { TreeItemProps, TreeSectionProps } from 'bref-ui/types';
	let items = $state<TreeItemProps[]>([]);
	let sections = $state<TreeSectionProps[]>([
		{
			id: 'workspace',
			label: 'Workspace',
			description: 'Projects and shared files',
			icon: {
				name: 'folder'
			},
			items: [
				{
					id: 'projects',
					label: 'Projects',
					icon: {
						name: 'folder'
					},
					children: [
						{
							id: 'design',
							label: 'Design',
							children: [
								{
									id: 'tokens',
									label: 'Tokens'
								},
								{
									id: 'components',
									label: 'Components'
								}
							]
						},
						{
							id: 'archive',
							label: 'Archive',
							disabled: true,
							children: [
								{
									id: 'past-work',
									label: 'Past work'
								}
							]
						}
					]
				}
			]
		},
		{
			id: 'personal',
			label: 'Personal',
			icon: {
				name: 'home'
			},
			items: [
				{
					id: 'settings',
					label: 'Settings',
					icon: {
						name: 'settings'
					}
				}
			]
		}
	]);
	let selection = $state<string | string[]>('tokens');
</script>

<TreeView
	{items}
	{sections}
	bind:selection
	label={'Interactive files'}
	size="medium"
	glass={false}
	defaultExpanded={true}
/>
<p role="status">
	Selected: {Array.isArray(selection) ? selection.join(', ') || 'none' : selection || 'none'}
</p>

Initially expanded remounts the preview because it sets each branch's initial state. Custom link nodes own their own interaction behavior.

Selecting an item does not expand its ancestors. Collapse Projects, then select Tokens to check that the branch stays closed.

Nested, disabled and empty states

Disabled Archive cannot be selected or deleted. Its disclosure remains available, and its child can be selected.

An empty tree renders no rows. Custom node snippets own their styling and behavior.

Props

Component props
PropTypeRequiredDefaultDescription
itemsTreeItemProps[]Yes—Nested items with id, label, optional icon, children and disabled. IDs must be unique across the component.
sectionsTreeSectionProps[]No[]Ordered headers with id, label, optional description and icon, and their own items array.
labelstringNoTree viewAccessible name for the group of items.
sizeBaseSizeNomediumsmall, medium or large.
glassbooleanNofalseUse glass on selected soft surfaces, including nested items.
selectionstring | string[]NoOmittedBindable selected IDs. A string selects one item; an array toggles individual items independently.
defaultExpandedbooleanNofalseSets the initial open state of each branch.
onDelete(id: string) => voidNoOmittedShows a delete button for each item; the caller updates the nested data and selection.
nodeSnippet<[TreeItemProps]>NoOmittedReplaces each top-level node in items and sections. The snippet owns its links, children, expansion, selection and actions.

Keyboard and accessibility

Tab and Shift+Tab move between selection buttons, delete buttons and disclosures. Enter or Space activates the focused control. Disabled buttons are skipped. Each branch has a separate expand/collapse button; arrow keys retain browser behavior.

The item label selects; the disclosure marker expands or collapses. An array enables independent multiple selection. Parent selection does not select descendants, and collapse preserves selection. The caller owns data removal and stale selected IDs.

This component uses grouped native buttons by default. Focus follows browser behavior when items disappear. Selection is bindable; native attributes are not forwarded. Supply a node snippet to render custom links or rows; the snippet owns the entire node, including descendants and interaction behavior. The documentation sidebar uses this slot for native links and the selected page's sections.