Skip to content

Getting started ​

This guide shows the most common setup: a monorepo with a VitePress site in docs/ and package Markdown files in packages/.

Example repository layout ​

txt
repo/
  docs/
    .vitepress/
      config.ts
    src/
      index.md
  packages/
    core/
      README.md
      CHANGELOG.md
    editor/
      README.md

The plugin reads Markdown under packages/ and writes generated pages under docs/src/generated/packages/.

txt
repo/
  docs/
    src/
      generated/
        packages/
          core.md
          core/changelog.md
          editor.md

Add the generated directory to .gitignore.

txt
docs/src/generated/

Generated Markdown is a build artifact. Your deploy pipeline should run vitepress build with this plugin enabled so generated pages are recreated before the site is built.

VitePress config ​

Use one shared externalMarkdownOptions object for both the Vite plugin and the sidebar helper. This keeps generated files and navigation metadata in sync.

ts
import { defineConfig } from 'vitepress'
import { externalMarkdown, getExternalMarkdownSidebar } from 'vitepress-plugin-external-markdown'

const externalMarkdownOptions = {
  root: new URL('..', import.meta.url).pathname,
  srcDir: 'src',
  sources: [
    {
      name: 'packages',
      baseDir: '../packages',
      pattern: '**/*.md',
    },
  ],
  outDir: 'generated/packages',
  routeBase: '/generated/packages/',
  resolveMarkdown(ctx) {
    const slug = ctx.relativePath
      .replace(/\/README\.md$/u, '')
      .replace(/\/index\.md$/u, '')
      .replace(/\.md$/u, '')
      .toLowerCase()

    return {
      slug,
      title: ctx.title,
      text: ctx.title,
      order: ctx.relativePath,
      sidebar: true,
      frontmatter: {
        editLink: false,
      },
    }
  },
}

export default defineConfig({
  srcDir: 'src',
  vite: {
    plugins: [externalMarkdown(externalMarkdownOptions)],
  },
  themeConfig: {
    sidebar: {
      '/generated/packages/': [
        {
          text: 'Packages',
          items: getExternalMarkdownSidebar(externalMarkdownOptions),
        },
      ],
    },
  },
})

Route result ​

With the layout above, generated routes look like this.

Source fileGenerated fileRoute
packages/core/README.mddocs/src/generated/packages/core.md/generated/packages/core
packages/core/CHANGELOG.mddocs/src/generated/packages/core/changelog.md/generated/packages/core/changelog
packages/editor/README.mddocs/src/generated/packages/editor.md/generated/packages/editor

Why root is set ​

When the config lives at docs/.vitepress/config.ts, this expression points to the VitePress project root, docs/.

ts
root: new URL('..', import.meta.url).pathname

This makes helper calls deterministic even when commands are run from the monorepo root, for example vitepress dev docs.