All skills
antfu avatar

/vitepress

@d02c484 official
by Anthony Fuantfu/skills5.9k stars
335

VitePress static site generator powered by Vite and Vue. Use when building documentation sites, configuring themes, or writing Markdown with Vue components.

Use this Skill: https://skilld.dev/gh/antfu/skills/vitepress

This session only. Nothing lands on disk.

referencesfeatures-dynamic-routes.md

≈1.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Dynamic Routes

Generate many pages from a single markdown file and dynamic data. Useful for blogs, package docs, or any data-driven pages.

Basic Setup

Create a template file with parameter in brackets and a paths loader:

.
└─ packages/
   ├─ [pkg].md           # Route template
   └─ [pkg].paths.js     # Paths loader

The paths loader exports a paths method returning route parameters:

// packages/[pkg].paths.js
export default {
  paths() {
    return [
      { params: { pkg: 'foo' }},
      { params: { pkg: 'bar' }},
      { params: { pkg: 'baz' }}
    ]
  }
}

Generated pages:

  • /packages/foo.html
  • /packages/bar.html
  • /packages/baz.html

Type-safe Loader with defineRoutes (v2)

In TypeScript, wrap the loader with defineRoutes for type hints on paths, watch, and transformPageData:

// packages/[pkg].paths.ts
import { defineRoutes } from 'vitepress'

export default defineRoutes({
  watch: ['../data/**/*.json'],
  async paths() {
    return [{ params: { pkg: 'foo' } }, { params: { pkg: 'bar' } }]
  },
  async transformPageData(pageData) {
    pageData.title = `${pageData.title} · Packages`
  }
})

defineRoutes is optional but recommended when authoring .paths.ts files.

Multiple Parameters

.
└─ packages/
   ├─ [pkg]-[version].md
   └─ [pkg]-[version].paths.js
// packages/[pkg]-[version].paths.js
export default {
  paths() {
    return [
      { params: { pkg: 'foo', version: '1.0.0' }},
      { params: { pkg: 'foo', version: '2.0.0' }},
      { params: { pkg: 'bar', version: '1.0.0' }}
    ]
  }
}

Dynamic Path Generation

From local files:

// packages/[pkg].paths.js
import fs from 'node:fs'

export default {
  paths() {
    return fs.readdirSync('packages').map(pkg => ({
      params: { pkg }
    }))
  }
}

From remote API:

// packages/[pkg].paths.js
export default {
  async paths() {
    const packages = await fetch('https://api.example.com/packages').then(r => r.json())
    
    return packages.map(pkg => ({
      params: {
        pkg: pkg.name,
        version: pkg.version
      }
    }))
  }
}

Accessing Params in Page

Template globals:

<!-- packages/[pkg].md -->
# Package: {{ $params.pkg }}

Version: {{ $params.version }}

In script:

<script setup>
import { useData } from 'vitepress'
const { params } = useData()
</script>

<template>
  <h1>{{ params.pkg }}</h1>
</template>

Passing Content

For heavy content (raw markdown/HTML from CMS), use content instead of params to avoid bloating the client bundle:

// posts/[slug].paths.js
export default {
  async paths() {
    const posts = await fetch('https://cms.example.com/posts').then(r => r.json())
    
    return posts.map(post => ({
      params: { slug: post.slug },
      content: post.content  // Raw markdown or HTML
    }))
  }
}

Render content in template:

<!-- posts/[slug].md -->
---
title: {{ $params.title }}
---

<!-- @content -->

The <!-- @content --> placeholder is replaced with the content from the paths loader.

Watch Option

Auto-rebuild when template or data files change:

// posts/[slug].paths.js
export default {
  watch: [
    './templates/**/*.njk',
    '../data/**/*.json'
  ],
  
  paths(watchedFiles) {
    const dataFiles = watchedFiles.filter(f => f.endsWith('.json'))
    
    return dataFiles.map(file => {
      const data = JSON.parse(fs.readFileSync(file, 'utf-8'))
      return {
        params: { slug: data.slug },
        content: renderTemplate(data)
      }
    })
  }
}

Complete Example: Blog

// posts/[slug].paths.js
import fs from 'node:fs'
import matter from 'gray-matter'

export default {
  watch: ['./posts/*.md'],
  
  paths(files) {
    return files
      .filter(f => !f.includes('[slug]'))
      .map(file => {
        const content = fs.readFileSync(file, 'utf-8')
        const { data, content: body } = matter(content)
        const slug = file.match(/([^/]+)\.md$/)[1]
        
        return {
          params: { 
            slug,
            title: data.title,
            date: data.date
          },
          content: body
        }
      })
  }
}
<!-- posts/[slug].md -->
---
layout: doc
---

# {{ $params.title }}

<time>{{ $params.date }}</time>

<!-- @content -->

Key Points

  • Template file uses [param] syntax in filename
  • Paths loader file must be named [param].paths.js or .ts
  • paths() returns array of { params: {...}, content?: string }
  • Use $params in templates or useData().params in scripts
  • Use content for heavy data to avoid client bundle bloat
  • watch enables HMR for template/data file changes
  • v2: wrap .paths.ts loaders with defineRoutes for type hints
<!-- Source references: - https://vitepress.dev/guide/routing#dynamic-routes -->

Source: SKILL.md on GitHub

No alerts2d5 checks · Risk SAFE
  • Gen Agent Trust Hub2d

    The skill provides comprehensive documentation and configuration examples for VitePress, a static site generator. No malicious patterns or intent were detected. A minor surface for indirect prompt injection was identified due to the documented features for ingesting external data during the site build process, which is inherent to the framework's functionality.

  • Socket2d

    No alerts

  • Snyk2d

    Risk: LOW · No issues

  • Runlayer7mo

    2/16 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at d02c484. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 4 days ago
Other metadata
metadata
{
  "author": "Anthony Fu",
  "version": "2026.9.25",
  "source": "Generated from https://github.com/vuejs/vitepress, scripts located at https://github.com/antfu/skills"
}

README badge

README badge for antfu/skills/vitepress

VitePress is a static site generator built on Vite and Vue 3 that converts Markdown files into a fast single-page application, with file-based routing and built-in support for Vue components directly in Markdown. Use it for documentation sites, blogs, and marketing pages where you need configurable themes, syntax-highlighted code blocks, and instant hot-reload during development.

Generated from the current SKILL.md.

Does VitePress work with Vue components embedded in Markdown?
Yes. Vue components work directly in Markdown files, and you can use script setup and directives within Markdown content.
Can I build a multi-language documentation site with VitePress?
Yes. VitePress includes internationalization support with locale configuration for building multi-language sites.
What search options does VitePress provide?
VitePress includes built-in local search or integration with Algolia for full-text search across documentation.
Can I customize the default theme or build a custom one from scratch?
Yes. You can extend the default theme via CSS variables and slots, or build a completely custom theme by implementing the theme interface.
Does VitePress support dynamic route generation?
Yes. You can generate pages from data at build time using createContentLoader and paths loader files for dynamic routing.

Generated from the current SKILL.md. These answers refresh after source changes.