From web-meta-framework-vitepress
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
How this skill is triggered — by the user, by Claude, or both
Slash command
/web-meta-framework-vitepress:web-meta-framework-vitepressThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Quick Guide:** VitePress is a Vue-powered static site generator built on Vite, designed for documentation. All config lives in `.vitepress/config.ts`. Use `defineConfig()` for type safety. Sidebar accepts arrays (single) or objects keyed by path prefix (multi-sidebar). Data loaders (`*.data.ts`) run at build time and ship only serialized results to the client. Vue components work directly i...
Quick Guide: VitePress is a Vue-powered static site generator built on Vite, designed for documentation. All config lives in
.vitepress/config.ts. UsedefineConfig()for type safety. Sidebar accepts arrays (single) or objects keyed by path prefix (multi-sidebar). Data loaders (*.data.ts) run at build time and ship only serialized results to the client. Vue components work directly in Markdown via<script setup>. Extend the default theme through layout slots and CSS variables rather than forking it.Current stable version: VitePress 1.6.x (2026). Uses Vite 6+ and Vue 3.5+.
<critical_requirements>
All code must follow project conventions in CLAUDE.md
(You MUST use .vitepress/config.ts with defineConfig() for all site configuration — VitePress does not support config outside .vitepress/)
(You MUST use data loader files (*.data.ts) for build-time data — never fetch data at runtime in SSR-unsafe ways)
(You MUST handle SSR compatibility — no bare window, document, or browser APIs outside onMounted or <ClientOnly>)
(You MUST extend the default theme via extends: DefaultTheme and layout slots — do not fork the entire theme)
(You MUST use createContentLoader() for markdown collection pages — it handles caching, watching, and frontmatter extraction)
</critical_requirements>
Auto-detection: VitePress, vitepress, .vitepress/config, defineConfig vitepress, createContentLoader, vitepress/theme, DefaultTheme, useData, useSidebar, markdown-it plugin vitepress, vitepress deploy
When to use:
createContentLoaderWhen NOT to use:
All configuration lives in .vitepress/config.ts. Use defineConfig() for type checking and autocompletion.
import { defineConfig } from "vitepress";
export default defineConfig({
title: "My Docs",
description: "Documentation site",
cleanUrls: true,
lastUpdated: true,
sitemap: { hostname: "https://docs.example.com" },
themeConfig: {
nav: [
{ text: "Guide", link: "/guide/" },
{ text: "API", link: "/api/" },
],
sidebar: {
/* see Pattern 2 */
},
socialLinks: [{ icon: "github", link: "https://github.com/org/repo" }],
search: { provider: "local" },
editLink: {
pattern: "https://github.com/org/repo/edit/main/docs/:path",
},
},
});
Why good: cleanUrls: true removes .html extensions, sitemap auto-generates sitemap.xml, lastUpdated reads git timestamps, search.provider: 'local' enables built-in search with zero config
Full examples: See examples/core.md for complete config, multi-sidebar, i18n, and markdown config.
Sidebar can be an array (global) or an object keyed by URL path prefix (multi-sidebar). Each section supports collapsed for expandable groups.
sidebar: {
'/guide/': [
{
text: 'Getting Started',
collapsed: false,
items: [
{ text: 'Introduction', link: '/guide/introduction' },
{ text: 'Installation', link: '/guide/installation' },
],
},
{
text: 'Advanced',
collapsed: true,
items: [
{ text: 'Data Loaders', link: '/guide/data-loading' },
{ text: 'Deployment', link: '/guide/deploy' },
],
},
],
'/api/': [
{
text: 'API Reference',
items: [
{ text: 'Config', link: '/api/config' },
{ text: 'Runtime API', link: '/api/runtime' },
],
},
],
}
Why good: Each path prefix gets its own sidebar navigation, collapsed: true keeps dense sidebars scannable
Common mistake: Using /guide without trailing slash — VitePress matches path prefixes, so /guide/ is more precise than /guide (which would also match /guidelines)
Data loaders (*.data.ts files) execute at build time. Results are serialized and shipped to client components. Use createContentLoader for Markdown collections, custom load() for arbitrary data.
// posts.data.ts
import { createContentLoader } from "vitepress";
export default createContentLoader("blog/posts/*.md", {
excerpt: true,
transform(rawData) {
return rawData
.sort(
(a, b) => +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date),
)
.map(({ url, frontmatter, excerpt }) => ({
title: frontmatter.title,
url,
date: frontmatter.date,
excerpt,
}));
},
});
<!-- blog/index.md — consume in Vue -->
<script setup>
import { data as posts } from "./posts.data";
</script>
<template>
<article v-for="post in posts" :key="post.url">
<h2>
<a :href="post.url">{{ post.title }}</a>
</h2>
<time>{{ post.date }}</time>
<div v-html="post.excerpt" />
</article>
</template>
Why good: createContentLoader handles file watching in dev, caching by mtime, frontmatter extraction, and optional HTML rendering. The transform strips unnecessary data so only what's needed reaches the client bundle.
Key options: includeSrc (raw markdown), render (full HTML), excerpt (content above first ---). Only enable what you need — render: true on hundreds of pages inflates the client bundle.
Full examples: See examples/core.md for custom loaders and
buildEndusage.
Vue components work directly in .md files. Use <script setup> for page-scoped imports. Register global components in the theme for cross-page reuse.
<script setup>
import StatusBadge from '../components/StatusBadge.vue'
</script>
# API Reference
<StatusBadge status="stable" /> This API is production-ready.
// .vitepress/theme/index.ts — global registration
import DefaultTheme from "vitepress/theme";
import StatusBadge from "../components/StatusBadge.vue";
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component("StatusBadge", StatusBadge);
},
};
Why good: Page-scoped imports enable code-splitting (only loaded on pages that use them). Global registration is for components used across many pages.
SSR rule: Components that access browser APIs must be wrapped in <ClientOnly> or guarded with onMounted. VitePress pre-renders all pages at build time.
Extend the default theme through layout slots and CSS variables. Do not fork the entire layout.
<!-- .vitepress/theme/MyLayout.vue -->
<script setup>
import DefaultTheme from "vitepress/theme";
import { useData } from "vitepress";
const { Layout } = DefaultTheme;
const { frontmatter } = useData();
</script>
<template>
<Layout>
<template #doc-before>
<div v-if="frontmatter.author" class="author-banner">
By {{ frontmatter.author }}
</div>
</template>
<template #doc-footer-before>
<div class="feedback-widget">Was this page helpful?</div>
</template>
</Layout>
</template>
Available layout slots: nav-bar-title-before, nav-bar-title-after, nav-bar-content-before, nav-bar-content-after, nav-screen-content-before, nav-screen-content-after, sidebar-nav-before, sidebar-nav-after, aside-top, aside-bottom, aside-outline-before, aside-outline-after, doc-before, doc-after, doc-footer-before, doc-top, doc-bottom, home-hero-before, home-hero-after, home-features-before, home-features-after, not-found
Full examples: See examples/core.md for CSS variable overrides and custom theme setup.
Build hooks in config transform page data, inject head tags, or generate files at build time.
export default defineConfig({
async transformPageData(pageData) {
// Add computed data available via useData()
pageData.frontmatter.head ??= [];
pageData.frontmatter.head.push([
"meta",
{ property: "og:title", content: pageData.title },
]);
},
async buildEnd(siteConfig) {
// Generate files after build — RSS feeds, redirects, etc.
const posts = await createContentLoader("blog/*.md").load();
// write to siteConfig.outDir
},
});
Available hooks: transformPageData (per-page, access frontmatter + route), transformHead (per-page, return head tags array), transformHtml (per-page, modify rendered HTML string), buildEnd (once after build, generate extra files)
VitePress extends standard Markdown with containers, code groups, line highlighting, and code snippets.
::: tip RECOMMENDATION
Use `createContentLoader` for blog index pages.
:::
::: code-group
```ts [config.ts]
export default defineConfig({ title: "Docs" });
```
export default { title: "Docs" };
:::
export default {
title: 'Highlighted', // highlighted
description: 'Also', // highlighted
}
<<< @/snippets/example.ts
**Container types:** `info`, `tip`, `warning`, `danger`, `details` (expandable). Customize labels in `markdown.container` config.
**Code annotations:** `// [!code focus]`, `// [!code ++]`, `// [!code --]`, `// [!code warning]`, `// [!code error]`
> **Full reference:** See [examples/core.md](examples/core.md#markdown-extensions) for all code block features and custom container labels.
---
### Pattern 8: markdown-it Plugin Integration
VitePress uses markdown-it internally. Add plugins via `markdown.config`.
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
markdown: {
// Built-in options
lineNumbers: true,
toc: { level: [1, 2, 3] },
// Add custom plugins
config: (md) => {
md.use(require('markdown-it-footnote'))
},
},
})
Why this matters: Plugins added via markdown.config get the fully-configured markdown-it instance with VitePress's own plugins already registered. Do not create a separate markdown-it instance.
Other resources:
<red_flags>
High Priority Issues:
window, document, or browser-only APIs outside onMounted or <ClientOnly> (SSR will crash at build time)render: true in createContentLoader for large collections (inflates client bundle with full HTML of every page).vitepress/config.ts (VitePress will not find it)fetch in components) for data that should be loaded at build time via data loadersMedium Priority Issues:
'/guide' matches /guidelines too)cleanUrls: true (results in .html extensions in all URLs)sitemap.hostname (sitemap generates with empty URLs)lastUpdated: true in site config AND having git history (feature is opt-in)Common Mistakes:
themeConfig.sidebar as a flat array when different sections need different sidebars (use object keyed by path prefix)vitepress build with the correct --base for subdirectory deploymentspaths() in [param].paths.ts<script setup> after content in Markdown (must be before any Markdown content for reliable parsing)useData() outside of Vue setup context (it is a composable, not a global function)Gotchas & Edge Cases:
createContentLoader only processes Markdown files — non-.md files matching the glob are silently skipped.data.ts (or .data.js, .data.mts, .data.mjs) — the .data suffix is requiredoutline accepts [2, 3] (array) or 'deep' (string) — numbers alone are invalidbase config must start and end with / (e.g., '/docs/') or VitePress will errorignoreDeadLinks: true only temporarily during migration</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST use .vitepress/config.ts with defineConfig() for all site configuration)
(You MUST use data loader files (*.data.ts) for build-time data — never fetch data at runtime in SSR-unsafe ways)
(You MUST handle SSR compatibility — no bare window, document, or browser APIs outside onMounted or <ClientOnly>)
(You MUST extend the default theme via extends: DefaultTheme and layout slots — do not fork the entire theme)
(You MUST use createContentLoader() for markdown collection pages — it handles caching, watching, and frontmatter extraction)
Failure to follow these rules will cause SSR build failures, bloated bundles, and broken upgrades.
</critical_reminders>
npx claudepluginhub agents-inc/skills --plugin web-meta-framework-vitepressBuilds and configures VitePress documentation sites with Markdown, Vue components, themes, and i18n. Use for creating docs, blogs, or marketing sites.
Generates and validates VitePress configuration files for technical documentation sites. Useful when setting up or modifying VitePress project settings.
Packages generated wiki Markdown files into a VitePress static site with dark theme and interactive Mermaid diagrams. Useful when users ask to build a site, run /deep-wiki:build, or want browsable HTML from wiki pages.