#Recursion#Trees

File Explorer

Build a nested folder tree from nothing — expand, collapse, and add files and folders at any depth.

By Pratik RaiMedium

Start with a single Create src button. From there every folder expands, collapses, and offers an Add control that asks for a type and a name — and folders created that way are themselves addable, so the tree can go arbitrarily deep.

Two companies asked this independently in 2025, which is a fair signal it is worth practising.

Two ideas carry it. The first is recursion in the component tree: one Node that renders a row and then renders itself for each child. Arbitrary depth then needs no per-level code, and the "new folders must also be addable" requirement becomes free rather than a second branch.

The second is immutable insertion, and it is where most attempts break. Finding the target folder, pushing onto its children array, and calling setRoot(root) re-renders nothing at all — React compares by reference and the root is the same object it already had. The data is then silently out of step with the screen, which is a confusing bug to chase. The fix is to rebuild the path: return a new object for the target folder and for every ancestor down to it, leaving untouched branches shared. That is the same structural sharing that Immer and hand-written Redux reducers rely on.

Whether expanded state belongs in each node or in a map at the top is a judgment call, and being able to say when you would move it — the moment a "collapse all" requirement appears — is part of the answer.

Goal: Render a recursive tree where any folder can be expanded and added to, arbitrarily deep.

Sources

Frequently asked questions

How do you render a tree of unknown depth in React?
With a component that renders itself for each child. One `Node` that draws a row and then maps its children to more `Node`s handles any depth without per-level code.
Why doesn't pushing to node.children re-render anything?
Because React compares by reference and the root object has not changed. Mutating a nested array and calling `setRoot(root)` hands React something it has already seen, so the render is skipped and the data silently diverges from the screen.
How do you insert into a nested tree immutably?
Rebuild the path: return a new object for the target folder and for every ancestor on the way down to it, leaving untouched branches shared. That structural sharing is what Immer and Redux reducers do.
Should expanded state live in each node or at the top?
Each node is fine while it is purely local UI state. It has to move up the moment a requirement needs to see across nodes — a collapse-all button, or expansion that persists — and knowing when to move it is the point.

Related Challenges

Continue learning with these related challenges

View All
React

Nested Tabs From a Flat Array

Given a flat array of items with parentId, render them as nested tabs at arbitrary depth.

React · JavaScriptPratik Rai ·

React

Sequential Progress Bars

Clicking Add appends a progress bar, and the bars fill strictly one at a time. A queueing problem disguised as an animation.

React · JavaScriptPratik Rai ·

React

Seat Booking Grid

A cinema seat picker: rows at different prices, sold seats, a selection limit and a running total.

React · JavaScriptPratik Rai ·