File Tree Explorer
A recursive, collapsible file tree with real roving-tabindex arrow-key navigation.
- Category
- Components
- Added
- 2026-09-01
- Deps
- 1
- Updated
- 2026-09-01
This is a viberdy Pro component
The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.
Props
"use client";
import { useEffect, useMemo, useRef, useState, type KeyboardEvent } from "react";
import { ChevronRight, File, Folder } from "lucide-react";
type Node = {
id: string;
name: string;
type: "folder" | "file";
children?: Node[];
};
const TREE: Node[] = [
{
id: "app", name: "app", type: "folder", children: [
{ id: "app-page", name: "page.tsx", type: "file" },
{ id: "app-layout", name: "layout.tsx", type: "file" },
{
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}About this component
A nested set of <ul>/<li> elements with onClick toggles looks like a file tree but has none of the keyboard behavior people expect from one — a real tree view uses roving tabindex, where only one row is ever a Tab stop and arrow keys move a virtual cursor between rows, exactly like VS Code's sidebar or a native OS file picker. Implementing that means flattening the nested Node structure into a visible-rows array every render (skipping any subtree whose parent folder isn't in the expanded set), tracking a single activeIndex against that flattened array, and syncing DOM focus to it from an effect. The non-obvious detail is ArrowLeft on a file: it has to walk backward through the flattened rows looking for the nearest row at a shallower depth, since the parent isn't stored on the node itself — the flattened list is the only place that relationship is recoverable. Caveat: this hand-rolled version doesn't virtualize, so a tree with thousands of expanded rows will re-flatten and re-render the whole visible set on every toggle; for that scale, pair the same expanded-Set logic with a virtualizer.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Nested Command Menu
componentsA Cmd+K menu with real submenu drilling, a breadcrumb trail, and scoped per-level search.
Inline Editable Grid
componentsA keyboard-driven spreadsheet grid with roving-cell navigation and Excel-style Enter/Tab commits.
Priority Plus Nav
componentsA nav that measures itself and collapses what does not fit into a More menu — ghost-row measurement, no breakpoints, no flicker loop.