srcM
main.tsx
App.tsxM
index.css
components
uiM
button.tsx
card.tsxM
toast.tsxA
header.tsx
footer.tsx
hooksU
lib
public
.envI
.eslintrc.cjs
index.html
package.jsonM
tsconfig.json
vite.config.ts
default.tsx
1'use client';23import { Files } from '@/components/ui/files';45type FileStatus = 'modified' | 'added' | 'untracked' | 'ignored' | undefined;67interface FileNode {8 type: 'file';9 name: string;10 status?: FileStatus;11}1213interface FolderNode {14 type: 'folder';15 name: string;16 status?: FileStatus;17 children: TreeNode[];18}1920type TreeNode = FileNode | FolderNode;2122const tree: TreeNode[] = [23 {24 type: 'folder',25 name: 'src',26 status: 'modified',27 children: [28 { type: 'file', name: 'main.tsx' },29 { type: 'file', name: 'App.tsx', status: 'modified' },30 { type: 'file', name: 'index.css' },31 {32 type: 'folder',33 name: 'components',34 children: [35 {36 type: 'folder',37 name: 'ui',38 status: 'modified',39 children: [40 { type: 'file', name: 'button.tsx' },41 { type: 'file', name: 'card.tsx', status: 'modified' },42 { type: 'file', name: 'toast.tsx', status: 'added' },43 ],44 },45 { type: 'file', name: 'header.tsx' },46 { type: 'file', name: 'footer.tsx' },47 ],48 },49 {50 type: 'folder',51 name: 'hooks',52 status: 'untracked',53 children: [54 { type: 'file', name: 'use-auth.ts', status: 'untracked' },55 { type: 'file', name: 'use-toast.ts', status: 'untracked' },56 ],57 },58 {59 type: 'folder',60 name: 'lib',61 children: [62 { type: 'file', name: 'cn.ts' },63 { type: 'file', name: 'utils.ts' },64 ],65 },66 ],67 },68 {69 type: 'folder',70 name: 'public',71 children: [{ type: 'file', name: 'favicon.svg' }],72 },73 { type: 'file', name: '.env', status: 'ignored' },74 { type: 'file', name: '.eslintrc.cjs' },75 { type: 'file', name: 'index.html' },76 { type: 'file', name: 'package.json', status: 'modified' },77 { type: 'file', name: 'tsconfig.json' },78 { type: 'file', name: 'vite.config.ts' },79];8081function renderNode(node: TreeNode) {82 if (node.type === 'file') {83 return <Files.File key={node.name} name={node.name} status={node.status} />;84 }85 return (86 <Files.Folder key={node.name} name={node.name} status={node.status}>87 {node.children.map((child) => renderNode(child))}88 </Files.Folder>89 );90}9192export function Default() {93 return <Files defaultValue="src/components/ui">{tree.map((node) => renderNode(node))}</Files>;94}
Installation
pnpm dlx nachui add filesAnatomy
1import { Files } from '@/components/ui/files';
1<Files defaultValue="src/components">2 <Files.Folder name="src">3 <Files.Folder name="components">4 <Files.File name="button.tsx" />5 <Files.File name="card.tsx" />6 </Files.Folder>7 <Files.File name="index.ts" />8 </Files.Folder>9 <Files.File name="package.json" />10</Files>
Features
- Expandable folders - Click to expand/collapse nested structures
- Git status indicators - Show file modification states
- Smooth animations - Animated expand/collapse transitions
- Auto-expand - Use
defaultValueto expand specific paths on load
API Reference
Files
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | string | - | Path to expand by default (e.g., "src/components") |
className | string | - | Additional CSS classes |
Files.Folder
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | Folder name |
path | string | - | Optional explicit path |
status | GitStatus | - | Git status indicator |
className | string | - | Additional CSS classes |
children | ReactNode | - | Nested files and folders |
Files.File
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | File name |
onClick | () => void | - | Click handler |
status | GitStatus | - | Git status indicator |
className | string | - | Additional CSS classes |
Git Status Types
Available status values:
'modified' | 'deleted' | 'added' | 'untracked' | 'renamed' | 'ignored'Each status has a unique color and letter indicator:
- M (Modified) - Yellow
- D (Deleted) - Red with strikethrough
- A (Added) - Green
- U (Untracked) - Green
- R (Renamed) - Blue
- I (Ignored) - Gray
Found something to improve?
Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.