Build a File Explorer UI
A recursive tree: a Node component that renders itself for children. State tracks which folders are expanded (a Set of ids). Folders toggle open/closed, files are leaves. Watch: recursion, expanded-state management, accessibility (role=tree/treeitem, keyboard nav), and lazy-loading large trees.
A file explorer is the canonical recursive component problem — a tree that renders itself.
The data and the recursive component
// data: { id, name, type: "file" | "folder", children?: Node[] }
function TreeNode({ node, expanded, onToggle, depth = 0 }) {
const isFolder = node.type === "folder";
const isOpen = expanded.has(node.id);
return (
<li role="treeitem" aria-expanded={isFolder ? isOpen : undefined}>
<div style={{ paddingLeft: depth * 16 }}
onClick={() => isFolder && onToggle(node.id)}>
{isFolder ? (isOpen ? "📂" : "📁") : "📄"} {node.name}
</div>
{isFolder && isOpen && (
<ul role="group">
{node.children.map((child) => (
<TreeNode key={child.id} node={child} {/* RECURSION */}
expanded={expanded} onToggle={onToggle} depth={depth + 1} />
))}
</ul>
)}
</li>
);
}The key idea: TreeNode renders TreeNode for each child — recursion handles arbitrary nesting depth.
Expanded state — lifted, a Set of ids
const [expanded, setExpanded] = useState(new Set());
const toggle = (id) =>
setExpanded((prev) => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next; // new Set — immutable update
});Keep expanded state lifted to the parent (a Set of expanded folder ids), not as local state inside each node — so it survives, can be controlled (expand-all, restore from URL), and one node doesn't own truth.
What's being graded
- Recursion — the component renders itself; clean base case (files are leaves).
- Expanded state management — a Set, lifted, updated immutably.
- Stable keys — node ids.
- Accessibility — it's a tree:
role="tree"on the root,role="treeitem"per node,role="group"for children,aria-expandedon folders. Keyboard nav: Up/Down to move, Right to expand/enter, Left to collapse/go to parent, Enter to open a file. - Indentation by depth.
Scaling considerations
- Lazy-loading — for huge trees (a real filesystem), don't load all children upfront; fetch a folder's children when it's first expanded.
- Virtualization — a deeply expanded tree can have thousands of visible rows; flatten the visible nodes and virtualize.
- Memoize
TreeNode— so toggling one folder doesn't re-render the whole tree. - Optional: selection, drag-and-drop to move, context menus, search/filter.
The framing
"It's a recursive component — a TreeNode that renders TreeNode for each child, so arbitrary depth just works, with files as the leaf base case. Expanded state is a Set of folder ids lifted to the parent and updated immutably — not local state per node, so it's controllable and persistent. The grading is recursion plus accessibility — the WAI-ARIA tree pattern with role=tree/treeitem/group, aria-expanded, and arrow-key navigation. For a real filesystem I'd lazy-load children on first expand and virtualize the visible rows."
Follow-up questions
- •How does the recursion handle arbitrary nesting depth?
- •Why lift expanded state to the parent instead of per-node?
- •What ARIA roles does the tree pattern need?
- •How would you handle a filesystem too large to load upfront?
Common mistakes
- •Local expanded state inside each node — can't control or persist it.
- •Mutating the Set instead of creating a new one.
- •No base case clarity / mishandling files vs folders.
- •Missing tree ARIA roles and keyboard navigation.
- •Loading and rendering a huge tree all at once.
Performance considerations
- •Memoize TreeNode so toggling one folder doesn't re-render siblings; lazy-load children on expand; virtualize the flattened visible-node list for large trees so the DOM stays bounded.
Edge cases
- •Empty folders.
- •Very deep nesting.
- •Huge trees needing lazy-load and virtualization.
- •Circular references in the data (shouldn't happen, but guard).
- •Keyboard nav across collapsed/expanded boundaries.
Real-world examples
- •VS Code's file explorer, OS file browsers, nested comment threads.
- •Category/menu trees in admin UIs.