|$ curl https://forge-ai.dev/api/markdown?path=docs/components/tree-view
$cat docs/tree-view.md
updated Recently·22 min read·published

Tree View

CSSHTMLTailwindBootstrapUIIntermediate🎯Free Tools
Introduction

A Tree View displays hierarchical data with expandable and collapsible nodes. It is used for file explorers, navigation sidebars, category browsers, and org charts. This guide covers production-ready tree patterns in plain HTML/CSS, Tailwind CSS, and Bootstrap — including icons, selection, disabled states, keyboard navigation, and ARIA semantics.

info

Use role="tree", role="treeitem", and aria-expanded so screen readers announce the structure correctly. Every interactive node must be focusable and operable by keyboard.
Basic Tree View

A minimal expandable tree. Click the chevron to expand or collapse a branch. CSS drives the caret rotation and group visibility through the aria-expanded attribute.

basic-tree
Live
untitled.html
HTML
1<ul class="tree" role="tree" aria-label="File explorer">
2 <li class="tree-node" role="treeitem" aria-expanded="true">
3 <div class="tree-row">
4 <button class="toggle" aria-label="Toggle src">
5 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
6 <path d="M6 9l6 6 6-6"/>
7 </svg>
8 </button>
9 <span class="label">
10 src
11 </span>
12 </div>
13 <ul class="tree-group" role="group">
14 <li class="tree-node" role="treeitem" aria-expanded="true">
15 <div class="tree-row">
16 <button class="toggle" aria-label="Toggle components">
17 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
18 <path d="M6 9l6 6 6-6"/>
19 </svg>
20 </button>
21 <span class="label">
22 components
23 </span>
24 </div>
25 <ul class="tree-group" role="group">
26 <li class="tree-node" role="treeitem">
27 <div class="tree-row">
28 <span class="spacer">
29 </span>
30 <span class="label">
31 Button.tsx
32 </span>
33 </div>
34 </li>
35 <li class="tree-node" role="treeitem">
36 <div class="tree-row">
37 <span class="spacer">
38 </span>
39 <span class="label">
40 TreeView.tsx
41 </span>
42 </div>
43 </li>
44 </ul>
45 </li>
46 <li class="tree-node" role="treeitem">
47 <div class="tree-row">
48 <span class="spacer">
49 </span>
50 <span class="label">
51 page.tsx
52 </span>
53 </div>
54 </li>
55 </ul>
56 </li>
57 <li class="tree-node" role="treeitem">
58 <div class="tree-row">
59 <span class="spacer">
60 </span>
61 <span class="label">
62 package.json
63 </span>
64 </div>
65 </li>
66 <li class="tree-node" role="treeitem">
67 <div class="tree-row">
68 <span class="spacer">
69 </span>
70 <span class="label">
71 README.md
72 </span>
73 </div>
74 </li>
75</ul>
preview
File Explorer with Icons

Inline SVG folder and file icons make the hierarchy scannable. The caret stays neutral while folder and file icons add color cues.

with-icons
Live
untitled.html
HTML
1<ul class="tree" role="tree" aria-label="File explorer">
2 <li class="tree-node" role="treeitem" aria-expanded="true">
3 <div class="tree-row">
4 <button class="toggle" aria-label="Toggle src">
5 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
6 <path d="M6 9l6 6 6-6"/>
7 </svg>
8 </button>
9 <svg class="icon folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10 <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
11 </svg>
12 <span class="label">
13 src
14 </span>
15 </div>
16 <ul class="tree-group" role="group">
17 <li class="tree-node" role="treeitem" aria-expanded="true">
18 <div class="tree-row">
19 <button class="toggle" aria-label="Toggle components">
20 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
21 <path d="M6 9l6 6 6-6"/>
22 </svg>
23 </button>
24 <svg class="icon folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
25 <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
26 </svg>
27 <span class="label">
28 components
29 </span>
30 </div>
31 <ul class="tree-group" role="group">
32 <li class="tree-node" role="treeitem">
33 <div class="tree-row">
34 <span class="spacer">
35 </span>
36 <svg class="icon file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
37 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
38 <polyline points="14 2 14 8 20 8"/>
39 <line x1="16" y1="13" x2="8" y2="13"/>
40 <line x1="16" y1="17" x2="8" y2="17"/>
41 <polyline points="10 9 9 9 8 9"/>
42 </svg>
43 <span class="label">
44 Button.tsx
45 </span>
46 </div>
47 </li>
48 <li class="tree-node" role="treeitem">
49 <div class="tree-row">
50 <span class="spacer">
51 </span>
52 <svg class="icon file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
53 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
54 <polyline points="14 2 14 8 20 8"/>
55 <line x1="16" y1="13" x2="8" y2="13"/>
56 <line x1="16" y1="17" x2="8" y2="17"/>
57 <polyline points="10 9 9 9 8 9"/>
58 </svg>
59 <span class="label">
60 TreeView.tsx
61 </span>
62 </div>
63 </li>
64 </ul>
65 </li>
66 <li class="tree-node" role="treeitem">
67 <div class="tree-row">
68 <span class="spacer">
69 </span>
70 <svg class="icon file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
71 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
72 <polyline points="14 2 14 8 20 8"/>
73 <line x1="16" y1="13" x2="8" y2="13"/>
74 <line x1="16" y1="17" x2="8" y2="17"/>
75 <polyline points="10 9 9 9 8 9"/>
76 </svg>
77 <span class="label">
78 page.tsx
79 </span>
80 </div>
81 </li>
82 </ul>
83 </li>
84 <li class="tree-node" role="treeitem">
85 <div class="tree-row">
86 <span class="spacer">
87 </span>
88 <svg class="icon file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
89 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
90 <polyline points="14 2 14 8 20 8"/>
91 <line x1="16" y1="13" x2="8" y2="13"/>
92 <line x1="16" y1="17" x2="8" y2="17"/>
93 <polyline points="10 9 9 9 8 9"/>
94 </svg>
95 <span class="label">
96 package.json
97 </span>
98 </div>
99 </li>
100</ul>
preview
Selection States

Single-select trees use aria-selected to communicate the active node. Click a row to select it; the green accent matches the ForgeLearn terminal aesthetic.

selection
Live
untitled.html
HTML
1<ul class="tree" role="tree" aria-label="Project settings">
2 <li class="tree-node" role="treeitem" aria-expanded="true">
3 <div class="tree-row">
4 <button class="toggle" aria-label="Toggle project">
5 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
6 <path d="M6 9l6 6 6-6"/>
7 </svg>
8 </button>
9 <span class="label">
10 forgelearn
11 </span>
12 </div>
13 <ul class="tree-group" role="group">
14 <li class="tree-node" role="treeitem" aria-selected="true">
15 <div class="tree-row">
16 <span class="spacer">
17 </span>
18 <span class="label">
19 General
20 </span>
21 </div>
22 </li>
23 <li class="tree-node" role="treeitem" aria-selected="false">
24 <div class="tree-row">
25 <span class="spacer">
26 </span>
27 <span class="label">
28 Members
29 </span>
30 </div>
31 </li>
32 <li class="tree-node" role="treeitem" aria-selected="false">
33 <div class="tree-row">
34 <span class="spacer">
35 </span>
36 <span class="label">
37 Billing
38 </span>
39 </div>
40 </li>
41 </ul>
42 </li>
43 <li class="tree-node" role="treeitem" aria-expanded="false">
44 <div class="tree-row">
45 <button class="toggle" aria-label="Toggle archived">
46 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
47 <path d="M6 9l6 6 6-6"/>
48 </svg>
49 </button>
50 <span class="label">
51 Archived
52 </span>
53 </div>
54 <ul class="tree-group" role="group">
55 <li class="tree-node" role="treeitem" aria-selected="false">
56 <div class="tree-row">
57 <span class="spacer">
58 </span>
59 <span class="label">
60 v1-docs
61 </span>
62 </div>
63 </li>
64 </ul>
65 </li>
66</ul>
preview
Disabled States

Nodes that are not interactive should set aria-disabled="true", remove the caret, and use muted colors. Disabled branches can still be expanded if their parent is enabled.

disabled-states
Live
untitled.html
HTML
1<ul class="tree" role="tree" aria-label="Permissions">
2 <li class="tree-node" role="treeitem" aria-expanded="true">
3 <div class="tree-row">
4 <button class="toggle" aria-label="Toggle organization">
5 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
6 <path d="M6 9l6 6 6-6"/>
7 </svg>
8 </button>
9 <span class="label">
10 Organization
11 </span>
12 </div>
13 <ul class="tree-group" role="group">
14 <li class="tree-node" role="treeitem" aria-selected="false">
15 <div class="tree-row">
16 <span class="spacer">
17 </span>
18 <span class="label">
19 Admins
20 </span>
21 </div>
22 </li>
23 <li class="tree-node" role="treeitem" aria-disabled="true">
24 <div class="tree-row disabled">
25 <span class="spacer">
26 </span>
27 <span class="label">
28 Guests (read-only)
29 </span>
30 </div>
31 </li>
32 </ul>
33 </li>
34 <li class="tree-node" role="treeitem" aria-expanded="false" aria-disabled="true">
35 <div class="tree-row disabled">
36 <button class="toggle" aria-label="Toggle legacy" disabled>
37 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
38 <path d="M6 9l6 6 6-6"/>
39 </svg>
40 </button>
41 <span class="label">
42 Legacy Projects
43 </span>
44 </div>
45 <ul class="tree-group" role="group">
46 <li class="tree-node" role="treeitem">
47 <div class="tree-row disabled">
48 <span class="spacer">
49 </span>
50 <span class="label">
51 v0-archive
52 </span>
53 </div>
54 </li>
55 </ul>
56 </li>
57</ul>
preview
Keyboard Navigation

Try using the arrow keys inside the preview. The WAI-ARIA tree pattern maps ArrowDown/ArrowUp to move between visible nodes, ArrowRight to expand or descend, ArrowLeft to collapse or ascend, and Home/End to jump to the first or last visible node.

keyboard-navigation
Live
untitled.html
HTML
1<ul class="tree" role="tree" aria-label="Keyboard navigable tree" tabindex="0">
2 <li class="tree-node" role="treeitem" aria-expanded="true">
3 <div class="tree-row" tabindex="-1">
4 <button class="toggle" aria-label="Toggle src">
5 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
6 <path d="M6 9l6 6 6-6"/>
7 </svg>
8 </button>
9 <span class="label">
10 src
11 </span>
12 </div>
13 <ul class="tree-group" role="group">
14 <li class="tree-node" role="treeitem" aria-expanded="true">
15 <div class="tree-row" tabindex="-1">
16 <button class="toggle" aria-label="Toggle components">
17 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
18 <path d="M6 9l6 6 6-6"/>
19 </svg>
20 </button>
21 <span class="label">
22 components
23 </span>
24 </div>
25 <ul class="tree-group" role="group">
26 <li class="tree-node" role="treeitem">
27 <div class="tree-row" tabindex="-1">
28 <span class="spacer">
29 </span>
30 <span class="label">
31 Button.tsx
32 </span>
33 </div>
34 </li>
35 <li class="tree-node" role="treeitem">
36 <div class="tree-row" tabindex="-1">
37 <span class="spacer">
38 </span>
39 <span class="label">
40 TreeView.tsx
41 </span>
42 </div>
43 </li>
44 </ul>
45 </li>
46 <li class="tree-node" role="treeitem">
47 <div class="tree-row" tabindex="-1">
48 <span class="spacer">
49 </span>
50 <span class="label">
51 page.tsx
52 </span>
53 </div>
54 </li>
55 </ul>
56 </li>
57 <li class="tree-node" role="treeitem" aria-expanded="false">
58 <div class="tree-row" tabindex="-1">
59 <button class="toggle" aria-label="Toggle public">
60 <svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
61 <path d="M6 9l6 6 6-6"/>
62 </svg>
63 </button>
64 <span class="label">
65 public
66 </span>
67 </div>
68 <ul class="tree-group" role="group">
69 <li class="tree-node" role="treeitem">
70 <div class="tree-row" tabindex="-1">
71 <span class="spacer">
72 </span>
73 <span class="label">
74 favicon.ico
75 </span>
76 </div>
77 </li>
78 </ul>
79 </li>
80</ul>
preview
Accessibility

Tree views are complex widgets. Follow the WAI-ARIA tree pattern so assistive technologies can announce levels, expansion, selection, and disabled states correctly.

  • Wrap the root in <ul role="tree"> and each node in <li role="treeitem">.
  • Set aria-expanded on branch nodes; omit it on leaf nodes.
  • Use aria-selected for single- or multi-select trees.
  • Use aria-disabled for non-interactive nodes instead of removing them.
  • Give the tree an accessible name with aria-label or aria-labelledby.
  • Implement arrow-key navigation: ArrowDown/Up, ArrowRight/Left, Home, End, Enter, and Space.
  • Ensure focus indicators are visible against the dark background.
$Blueprint — Engineering Documentation·Section ID: UI-TV-01·Revision: 2.0

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.