/

File Trees in React: A Picture or a Control

Two components share the name and share nothing else. The keyboard contract for the interactive one, and the three rules that keep a large tree usable.

Serafim Korablev
Serafim Korablev
@korablev

A file tree is two components wearing one name. One is a picture: the folder structure of a project, shown on a documentation page so a reader can see where files go. The other is a control: an explorer someone navigates, selects in and acts on. They share a shape and share almost nothing else.

31 public components on 21st have "tree" in the name. Deciding which of the two you are building is the whole job.

The picture

Static, decorative, usually beside an install command. File Tree by dillionverma and File Tree by Jatin Yadav are the documentation shapes, the second with expand and collapse.

For this version, keep it simple and keep it text. It is a nested list, and its whole value is that a reader can scan it and copy a path out of it. Do not put it in a canvas, do not animate it, and make sure the file names are selectable.

The control

An explorer: keyboard navigable, selectable, sometimes with drag and drop and a context menu. Tree View by preetsuthar17 and Tree by Hayden Bleasel are the interactive versions.

This one has a real specification, and it is the most demanding keyboard contract of any common component. One tab stop for the whole tree, arrows to move: down and up through visible nodes, right to expand or descend, left to collapse or ascend. Typeahead jumps to a node by name. Home and End go to the ends. Every node exposes its level, its position among siblings and whether it is expanded.

That is a lot, and it is the reason to take the behaviour from a library rather than write it. A tree built from nested divs with click handlers is unusable without a mouse, and unlike a menu, nobody notices until someone tries.

Performance, which arrives sooner than you think

A repository tree with ten thousand nodes rendered eagerly is ten thousand DOM elements before anyone expands anything. Three rules keep it usable.

Render only expanded branches. A collapsed folder's children should not exist in the DOM at all.

Virtualise the visible rows past a few hundred, the same as the data table guide describes, remembering that it breaks find-in-page.

Load children on demand for anything backed by a network. An explorer that fetches the whole tree to show the root is the slowest possible first paint.

The details that make it feel right

Indentation guides. Vertical lines at each level, drawn with a border on a pseudo-element rather than as elements, because a deep tree is otherwise impossible to read.

Icons that say something. A folder open and closed, and a file icon by extension if you have one. Decorative, so aria-hidden, with the name carrying the meaning.

Selection separate from expansion. Clicking the chevron expands; clicking the name selects. Conflating them makes browsing impossible without changing the selection.

A stable order. Folders before files, then alphabetical. Whatever the API returns is not an order.

Where else to look

The honest list, because the answer is not always us:

SourceBest forTrade-off
A nested <ul>The documentation picture, which is most usesNot a control
React AriaThe full tree keyboard and screen reader contractMore API, and the visuals are yours
TanStack VirtualLarge trees at a stable frame rateBreaks find-in-page
21stDocumentation trees and interactive explorersQuality varies by author, so preview before you take it

Taking one

Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:

bash

That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue.

Browse components →

Frequently asked

What is the keyboard contract for a tree view?
One tab stop for the whole tree, arrows to navigate: down and up through visible nodes, right to expand or descend, left to collapse or ascend. Typeahead jumps by name, Home and End reach the ends, and each node exposes its level, position among siblings and expanded state.
How do you keep a large file tree fast?
Do not render collapsed branches at all, virtualise the visible rows past a few hundred, and load children on demand when the tree is backed by a network. Fetching an entire repository tree to display the root is the slowest possible first paint.
Should clicking a folder name expand it?
Keep selection and expansion separate: the chevron expands, the name selects. Conflating them makes it impossible to browse the structure without changing what is selected, which is the most common complaint about hand-built explorers.
What does a documentation file tree need?
Much less. It is a nested list whose value is that a reader can scan it and copy a path out of it, so keep the names as real selectable text, skip the animation, and never render it into a canvas.

Published

Aug 21, 2026

Read time

4 min

Tags

GuideTreesReactAccessibility

Share