Interactive file/folder tree diagrams for VitePress. Write YAML or paste ASCII — get a beautiful, collapsible Vue component.
| Category | What you get |
|---|---|
| Input | YAML, ASCII (├── └──), external URL (YAML/JSON/JS), filesystem scan (from:) |
| Interaction | Expand/collapse folders, keyboard nav, search & filter, context menu (right-click) |
| Toolbar | Expand all, Collapse all, Copy as text, Download (.txt/.yaml/.json), Shell script (mkdir/touch) |
| Visual | Guide lines, hover branch highlight, smooth animations, dark mode |
| Metadata | description badge, note, highlight, icon (emoji/Iconify), href links |
| Git status | added / modified / deleted indicators with color-coded badges |
| Tabs | tab field on root nodes → Chrome-style switchable tabs |
| Deep linking | URL hash → auto-expand, scroll, pulse animation |
| Preview | preview field → tooltip on hover (600 ms delay) |
| Icons | Per-extension SVG, custom emoji, Iconify classes, global iconMap |
| A11y | ARIA attributes, prefers-reduced-motion, keyboard navigation |
# pnpm
pnpm add vitepress-plugin-folder-tree
# npm
npm install vitepress-plugin-folder-tree
# yarn
yarn add vitepress-plugin-folder-tree1. VitePress config — .vitepress/config.mts:
import { defineConfig } from 'vitepress'
import { withFolderTree } from 'vitepress-plugin-folder-tree'
export default withFolderTree(
defineConfig({ title: 'My Docs' })
)2. Import styles — .vitepress/theme/index.ts:
import DefaultTheme from 'vitepress/theme'
import 'vitepress-plugin-folder-tree/style.css'
export default {
extends: DefaultTheme,
}CSS is shipped as a separate file for better performance, caching, and CSP compatibility.
Write a fenced code block with language tree (or folder-tree / file-tree):
```tree
- name: src
children:
- name: components
children:
- Button.vue
- Modal.vue
- App.vue
- main.ts
- package.json
```Each root item is a string (file) or object (file/folder with metadata):
```tree
- name: src
description: "Source code"
children:
- name: index.ts
highlight: true
- utils.ts
- README.md
```Paste tree command output — auto-detected by ├── / └── characters:
```tree
├── src
│ ├── index.ts
│ └── utils.ts
├── package.json
└── README.md
```
Load tree from a separate file (YAML, JSON, or JS module). All URL resolution happens at build time — no client-side fetching:
```tree
url: /trees/my-project.yaml
```JS modules run in Node.js at build time, so you can use fs.readFileSync, fetch() for remote APIs, or any Node API:
// public/trees/my-project.js
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
export default function () {
const __dir = dirname(fileURLToPath(import.meta.url));
const data = JSON.parse(readFileSync(join(__dir, 'data.json'), 'utf-8'));
return [{ name: 'project', children: data.files }];
}Generate tree from a real directory at build time:
```tree
from: ./src
depth: 3
exclude:
- "*.test.ts"
```With file content preview (hover to see, click to copy):
```tree
from: ./src
preview: true
maxPreviewSize: 4096
openDepth: 2
```| Option | Type | Default | Description |
|---|---|---|---|
from |
string |
— | Directory path to scan |
depth |
number |
10 |
Max directory nesting depth |
preview |
boolean |
false |
Read file contents into tooltip |
maxPreviewSize |
number |
4096 |
Max file size (bytes) for preview |
previewExtensions |
string[] |
common text exts | Which file extensions to read |
openDepth |
number |
-1 (all) |
Folder auto-open depth (0 = all closed) |
exclude |
string[] |
built-in list | Glob patterns to exclude |
include |
string[] |
[] (all) |
Glob patterns to include |
showRoot |
boolean |
true |
Wrap in root folder node |
name |
string |
dir name | Custom root folder name |
| Field | Type | Default | Description |
|---|---|---|---|
name |
string |
required* | File or folder name |
type |
'file' | 'folder' |
auto | Force node type (auto: has children → folder) |
children |
array |
— | Nested items (makes this node a folder) |
description |
string |
— | Badge shown next to name |
note |
string |
— | Right-aligned label |
highlight |
boolean |
false |
Accent background on the row |
icon |
string |
— | Custom emoji or Iconify class |
open |
boolean |
defaultOpen |
Initial expanded state |
locked |
boolean |
false |
Prevent toggling this folder |
href |
string |
— | URL — clicking the name navigates here |
status |
'added' | 'modified' | 'deleted' |
— | Git-style diff badge (A/M/D) |
preview |
string |
— | Tooltip text on hover (600 ms delay) |
tab |
string |
— | Root-level tab grouping |
*
nameis optional whentabis set — the tab label is used as the name.
- name: config.ts
status: modified
- name: new-feature.ts
status: added
- name: old-module.ts
status: deleted| Status | Visual |
|---|---|
added |
Green text + A badge |
modified |
Amber text + M badge |
deleted / removed |
Red text + D badge + strikethrough |
Use tab on root-level nodes to create switchable views:
```tree
- tab: "Frontend"
children:
- name: src
children:
- App.vue
- main.ts
- package.json
- tab: "Backend"
children:
- name: src
children:
- index.ts
- server.ts
- package.json
```Each tab shows the children of its root node. All toolbar operations (search, copy, download, shell script) work within the active tab.
Append #path/to/file to the page URL:
https://example.com/docs/guide.html#src/components/Button.vue
The tree will:
- Switch to the correct tab (if tabs are used)
- Expand all ancestor folders
- Scroll to the node
- Highlight it with a pulse animation
Add preview to show code/text in an IDE-style tooltip on hover:
- name: index.ts
preview: "import { createApp } from 'vue'\ncreateApp(App).mount('#app')"Tooltip appears after 600 ms, disappears on mouse leave.
Click the >_ button in the toolbar to copy a shell script (mkdir -p + touch) that recreates the tree structure. Paste into your terminal to scaffold a project instantly.
- name: .env
icon: "\U0001F510"Map filenames or extensions to emoji/Iconify classes:
export default withFolderTree(
defineConfig({}),
{
iconMap: {
'vite.config.ts': '\u26A1',
'ts': 'logos:typescript-icon',
'vue': 'logos:vue',
},
}
)Priority: full filename → extension → default SVG.
| Field | Type | Default | Description |
|---|---|---|---|
from |
string |
required | Path to scan (relative to project root) |
depth |
number |
10 |
Max scan depth |
name |
string |
dir name | Override root folder display name |
showRoot |
boolean |
true |
Wrap in root folder |
exclude |
string[] |
— | Glob patterns to skip |
include |
string[] |
— | Glob patterns for files (folders always traversed) |
Default excludes: node_modules, .git, .DS_Store, dist, .cache, .vitepress, __pycache__, .next, .nuxt, .idea, .vscode, coverage, and more.
```tree
from: ./src
include:
- "*.ts"
- "*.tsx"
exclude:
- "*.test.ts"
options:
defaultOpen: false
```Access the context menu on any node:
| Method | How |
|---|---|
| Right-click | Standard right mouse button |
| Ctrl + Click | Hold Ctrl (or Cmd on Mac) and left-click |
| Shift + F10 | Focus a row with arrow keys, then press Shift + F10 |
Menu actions:
- Copy Name — file/folder name (e.g.
Button.vue) - Copy Path — full tree path (e.g.
src/components/Button.vue)
Press Escape to close.
export default withFolderTree(
defineConfig({}),
{
languages: ['tree', 'file-tree', 'folder-tree'], // trigger languages
defaultOpen: true, // folders expanded by default
showToolbar: true, // show toolbar
showBadges: true, // auto file/folder count badges
interactive: true, // allow expand/collapse
root: process.cwd(), // base path for `from:` resolution
iconMap: {}, // global icon overrides
}
)| Option | Type | Default | Description |
|---|---|---|---|
languages |
string[] |
['folder-tree', 'file-tree', 'tree'] |
Code block languages |
defaultOpen |
boolean |
true |
Default expand state |
showToolbar |
boolean |
true |
Toolbar visibility |
showBadges |
boolean |
true |
Auto count badges |
interactive |
boolean |
true |
Allow interaction |
root |
string |
process.cwd() |
Root for from: paths |
iconMap |
Record<string, string> |
{} |
Filename/ext → icon mapping |
Override global settings per code block:
```tree
options:
defaultOpen: false
showToolbar: false
showBadges: false
interactive: false
tree:
- name: src
children:
- index.ts
```| Key | Action |
|---|---|
↓ / ↑ |
Move focus between rows |
→ |
Expand focused folder |
← |
Collapse focused folder |
Enter / Space |
Toggle folder open/close |
Shift + F10 |
Open context menu for focused row |
Escape |
Close context menu / tooltip |
Tab |
Enter / exit tree area |
| Modifier | Action |
|---|---|
Ctrl + Click |
Open context menu (Win/Linux) |
Cmd + Click |
Open context menu (Mac) |
| Icon | Action |
|---|---|
▼ |
Expand all folders |
▶ |
Collapse all folders |
| Copy | Copy tree as ASCII text |
↓ |
Download as .txt / .yaml / .json |
>_ |
Copy shell script (mkdir + touch) |
| Search | Filter nodes by name/description/note |
import {
validateTreeInput,
parseTree,
parseAsciiTree,
scanDirectory,
} from 'vitepress-plugin-folder-tree'
// Validate YAML
const result = validateTreeInput(yamlString)
// { valid: boolean, errors: string[], warnings: string[] }
// Parse YAML → TreeNode[]
const { tree, options } = parseTree(yamlString)
// Parse ASCII → TreeNode[]
const nodes = parseAsciiTree(asciiString)
// Scan filesystem → TreeNode[]
const tree = scanDirectory('/path', 5, ['node_modules'], ['*.ts'])pnpm install
pnpm build # build plugin
pnpm dev # dev server with examples
pnpm docs:build # build example docs