NNicheStackTutorials
← All tutorials
VitePress

Auto-Generate VitePress Sidebar from File Structure

Filesystem-driven sidebar with glob — stop hand-editing config when adding pages.

·12 min read·VitePress
Auto-Generate VitePress Sidebar from File Structure cover

Tested on: VitePress 1.6.3, Node 20, 12 sections / 340 files Problem: Every new page required editing config.mts.

Install glob helper

npm i -D fast-glob

Generator module

.vitepress/utils/sidebar.mjs:

import fg from 'fast-glob'
import path from 'node:path'
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const DOCS = path.resolve(__dirname, '../../docs')

function titleFromFile(file) {
  const raw = fs.readFileSync(path.join(DOCS, file), 'utf8')
  const m = raw.match(/^title:\s*(.+)$/m)
  if (m) return m[1].trim().replace(/["']/g, '')
  return path.basename(file, '.md').replace(/-/g, ' ')
}

export function sidebarFor(prefix) {
  const files = fg.sync(`${prefix}/**/*.md`, {
    cwd: DOCS,
    ignore: ['**/index.md']
  }).sort()
  return files.map(f => ({
    text: titleFromFile(f),
    link: '/' + f.replace(/\.md$/, '')
  }))
}

Wire into config

.vitepress/config.mts:

import { sidebarFor } from './utils/sidebar.mjs'

export default defineConfig({
  themeConfig: {
    sidebar: {
      '/guide/': sidebarFor('guide'),
      '/api/': sidebarFor('api')
    }
  }
})

Nested groups

export function nestedSidebar(prefix) {
  const files = fg.sync(`${prefix}/**/*.md`, { cwd: DOCS })
  const groups = new Map()
  for (const f of files) {
    const parts = f.split('/')
    if (parts.length <= 2) continue
    const group = parts[1]
    if (!groups.has(group)) {
      groups.set(group, { text: group.replace(/-/g, ' '), collapsed: false, items: [] })
    }
    groups.get(group).items.push({
      text: titleFromFile(f),
      link: '/' + f.replace(/\.md$/, '')
    })
  }
  return [...groups.values()]
}

Errors

Sidebar link 404

Enable cleanUrls: true in config.

Cannot find module sidebar.mjs

Use .mjs + fileURLToPath for __dirname.

Verify

node -e "import('./.vitepress/utils/sidebar.mjs').then(m => console.log(m.sidebarFor('guide').length))"
npm run docs:dev
# add new .md → sidebar updates automatically

Result

Zero manual sidebar PRs. Config load +200ms — negligible.

Tags: VitePressSidebarAutomationConfig