npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vitepress-plugin-folder-tree

v1.2.1

Published

Interactive, collapsible file/folder tree diagrams for VitePress — supports YAML, ASCII, external URLs, filesystem scan, tabs, search, git status badges, deep linking, and more

Readme

npm version npm downloads License Donate

vitepress-plugin-folder-tree

Interactive file/folder tree diagrams for VitePress. Write YAML or paste ASCII — get a beautiful, collapsible Vue component.


Features

| 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 |


Quick Start

Install

# pnpm
pnpm add vitepress-plugin-folder-tree

# npm
npm install vitepress-plugin-folder-tree

# yarn
yarn add vitepress-plugin-folder-tree

Configure

1. 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.

Use

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
```

Input Formats

YAML (structured)

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
```

ASCII (paste from terminal)

Paste tree command output — auto-detected by ├── / └── characters:

```tree
├── src
│   ├── index.ts
│   └── utils.ts
├── package.json
└── README.md
```

External URL

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 }];
}

Filesystem scan

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 |


Node Fields

| 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 |

* name is optional when tab is set — the tab label is used as the name.


Diff / Git Status

- 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 |


Tabs

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.


Deep Linking

Append #path/to/file to the page URL:

https://example.com/docs/guide.html#src/components/Button.vue

The tree will:

  1. Switch to the correct tab (if tabs are used)
  2. Expand all ancestor folders
  3. Scroll to the node
  4. Highlight it with a pulse animation

Preview Tooltip

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.


Shell Script Generation

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.


Custom Icons

Per-node icon

- name: .env
  icon: "\U0001F510"

Global iconMap

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.


Auto-generate from Filesystem

| 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
```

Context Menu

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.


Plugin Configuration

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 |


Per-block Options

Override global settings per code block:

```tree
options:
  defaultOpen: false
  showToolbar: false
  showBadges: false
  interactive: false
tree:
  - name: src
    children:
      - index.ts
```

Keyboard Navigation

| 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 keys

| Modifier | Action | |---|---| | Ctrl + Click | Open context menu (Win/Linux) | | Cmd + Click | Open context menu (Mac) |


Toolbar Buttons

| 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 |


Programmatic API

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'])

Development

pnpm install
pnpm build        # build plugin
pnpm dev          # dev server with examples
pnpm docs:build   # build example docs

Links


License

MIT © s00d