javascript
47 lines · 8 steps
How a recursive file tree renders in React
A component renders itself for each child to draw an expandable, accessible folder tree of arbitrary depth.
Explained by
highlit
1import { useState } from 'react';
2
3function TreeNode({ node, depth = 0 }) {
4 const [expanded, setExpanded] = useState(depth === 0);
5 const isFolder = node.type === 'folder';
6 const hasChildren = isFolder && node.children?.length > 0;
7
8 const toggle = () => {
9 if (hasChildren) setExpanded((prev) => !prev);
10 };
11
12 return (
13 <li role="treeitem" aria-expanded={hasChildren ? expanded : undefined}>
14 <div
15 className="tree-row"
16 style={{ paddingLeft: depth * 16 }}
17 onClick={toggle}
18 >
19 <span className="tree-caret">
20 {hasChildren ? (expanded ? '▾' : '▸') : ''}
21 </span>
22 <span className="tree-icon">
23 {isFolder ? (expanded ? '📂' : '📁') : '📄'}
24 </span>
25 <span className="tree-label">{node.name}</span>
26 </div>
27
28 {hasChildren && expanded && (
29 <ul role="group" className="tree-group">
30 {node.children.map((child) => (
31 <TreeNode key={child.id} node={child} depth={depth + 1} />
32 ))}
33 </ul>
34 )}
35 </li>
36 );
37}
38
39export default function TreeView({ root }) {
40 return (
41 <ul role="tree" className="tree-view">
42 {root.map((node) => (
43 <TreeNode key={node.id} node={node} />
44 ))}
45 </ul>
46 );
47}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A component that renders itself is the natural fit for tree-shaped data of unknown depth.
- 2Holding expand/collapse state per node keeps each subtree independent instead of tracking one global map.
- 3Passing an incremented depth down lets each level compute its own indentation without a parent coordinating it.
Related explainers
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 steps
typescript
import { useEffect, useState } from "react"; interface Section { id: string;
Building a scroll-spy hook in React
custom-hooks
intersectionobserver
dom-observation
Intermediate
8 steps
javascript
import { useEffect, useRef, useState } from 'react'; export function useDelayedFlag(active, delay = 300) { const [visible, setVisible] = useState(false);
Delaying a loading spinner with a React hook
custom-hooks
debouncing
cleanup
Intermediate
8 steps
javascript
const SWIPE_THRESHOLD = 80; const MAX_TRANSLATE = 120; export function attachSwipeToDismiss(element, onDismiss) {
Building a swipe-to-dismiss gesture in JS
touch-events
gesture-detection
dom-manipulation
Intermediate
10 steps
javascript
const { pool } = require('./db'); function withTransaction() { return async (req, res, next) => {
A per-request transaction middleware in Express
middleware
database-transactions
connection-pooling
Advanced
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-a-recursive-file-tree-renders-in-react-explained-javascript-df63/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.