@pierre/trees is an open source file tree rendering library. It's built for performance and flexibility, is super customizable, and comes packed with features. Made with love by The Pierre Computer Company.
Currently v1.0.0-beta.4
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950import type { ButtonHTMLAttributes, ReactNode } from 'react';
import { cn } from '../utils/cn';
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { variant?: 'primary' | 'secondary' | 'ghost' | 'danger'; size?: 'sm' | 'md' | 'lg'; isLoading?: boolean; children: ReactNode;}
const VARIANT_CLASSES: Record<NonNullable<ButtonProps['variant']>, string> = { primary: 'bg-brand-600 text-white hover:bg-brand-500', secondary: 'bg-zinc-200 text-zinc-900 hover:bg-zinc-300', ghost: 'bg-transparent text-zinc-200 hover:bg-white/10', danger: 'bg-danger text-white hover:brightness-110',};
const SIZE_CLASSES: Record<NonNullable<ButtonProps['size']>, string> = { sm: 'px-2 py-1 text-xs', md: 'px-3 py-1.5 text-sm', lg: 'px-4 py-2 text-base',};
export function Button({ variant = 'primary', size = 'md', isLoading = false, className, disabled, children, ...rest}: ButtonProps) { return ( <button {...rest} disabled={disabled ?? isLoading} aria-busy={isLoading || undefined} className={cn( 'inline-flex items-center justify-center rounded font-medium transition', 'disabled:cursor-not-allowed disabled:opacity-60', VARIANT_CLASSES[variant], SIZE_CLASSES[size], className, )} > {children} </button> );}Enable the flattenEmptyDirectories boolean option in tree options to collapse single-child folder chains into one row for a more compact tree. Tree-shape options…
Use the gitStatus option to show status badges for added, modified, deleted, renamed, untracked, and ignored files. Ignored items inherit their styling without rendering an indicator while folders with changed descendants get a dot indicator automatically.
| Indicator | State | Description |
|---|---|---|
| M | modified | Tracked file with uncommitted changes |
| A | added | New file staged in the working tree |
| D | deleted | Tracked file removed from the working tree |
| R | renamed | Tracked file moved or renamed |
| U | untracked | New file not yet tracked by Git |
None | ignored | Path excluded by gitignore; inherits muted styling |
| ● | descendant | Folder contains changed descendants |
Render your own custom context menu with composition.contextMenu and the React renderContextMenu prop. This demo exposes trigger modes for right-click, trigger button, or both, and menu actions for new files, new folders, rename, and delete. This demo uses Shadcn UI components for the context menu as an example. Your app can use the menus that you already have.
Move files and folders by dragging them onto other folders, flattened folders, or the root with dragAndDrop: true. Drop targets open automatically when you hover, and dragging is disabled while search is active. Pass a canDrag callback to prevent specific paths from being dragged. Learn more in the item actions guide.
Filter the tree by typing in the search field. Search across file paths and names. Trees includes three fileTreeSearchMode options to control how non-matching items are shown. All three demos below start with search prepopulated to show the different modes.
Trees with tens of thousands of items render instantly with built-in and automatic virtualization. Only visible rows are mounted. The tree below contains 2,956 files with every folder expanded. Shown with stickyFolders enabled.
With built-in keyboard navigation, focus management, and ARIA roles (tree, treeitem) plus aria-level, aria-posinset, and aria-setsize attributes, Trees are immediately accessible to all users. We've designed Trees to align with WCAG 2.1 guidance.
| Key | Action |
|---|---|
| ↑↓ | Move focus between items |
| → | Expand folder or move to first child |
| ← | Collapse folder or move to parent |
| EnterSpace | Select focused item; toggle folder |
| ⌘/CtrlSpace | Add or remove focused item from selection |
| a–z | Type-ahead to jump by name |
| Tab | Focus in/out of tree, between search and tree |
Choose between the shipped minimal, standard, and complete icon tiers. Each tier is cumulative. Override the built-in palette with CSS variables like --trees-file-icon-color-javascript, or fall back to a fully custom sprite. See the FileTreeIconConfig reference for the full API.
Want matching file icons in your editor? Install Pierre Icons for VS Code to bring the same icon set into your sidebar.
The same Shiki themes used by @pierre/diffs can style the FileTree. Sidebar and Git decoration colors come from your choice of themes. Pick a theme and switch light/dark to see the tree update live. See the styling and theming reference for more.
Love the Pierre themes? Install our Pierre Theme pack with light and dark flavors, or learn how to build your own Shiki themes.
Modify CSS custom properties via the style prop to override UI and theme colors. For example, below are three examples—custom light, dark, and Synthwave '84—that override our default values and the CSS we use to style the tree. See the styling and theming reference for more info.
We’re using OKLCH colors here—a modern color space that allows for more uniform colors and more consistent palettes.
Pass density="compact", "default", or "relaxed" (or a custom numeric factor) to useFileTree to tune the tree's proportions in one place — the keyword resolves both the row height and the spacing factor. See the styling and theming reference for more info.
Collectively, our team brings over 150 years of expertise designing, building, and scaling the world's largest distributed systems at Cloudflare, Coinbase, Discord, GitHub, Reddit, Stripe, X, and others.