Files
agent-skills/skills/vitepress/references/theme-custom.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

270 lines
5.0 KiB
Markdown

---
name: vitepress-custom-themes
description: Building custom themes from scratch with the theme interface, Layout component, and enhanceApp
---
# Custom Themes
Build a theme from scratch when the default theme doesn't fit your needs.
## Theme Entry
Create `.vitepress/theme/index.ts`:
```ts
// .vitepress/theme/index.ts
import Layout from './Layout.vue'
export default {
Layout,
enhanceApp({ app, router, siteData }) {
// Register global components, plugins, etc.
}
}
```
## Theme Interface
```ts
interface Theme {
// Required: Root layout component
Layout: Component
// Optional: Enhance Vue app instance
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
// Optional: Extend another theme
extends?: Theme
}
interface EnhanceAppContext {
app: App // Vue app instance
router: Router // VitePress router
siteData: Ref<SiteData> // Site-level metadata
}
```
## Basic Layout
The Layout component must render `<Content />` for markdown:
```vue
<!-- .vitepress/theme/Layout.vue -->
<script setup>
import { useData } from 'vitepress'
const { page, frontmatter } = useData()
</script>
<template>
<div class="layout">
<header>
<nav>My Site</nav>
</header>
<main>
<div v-if="page.isNotFound">
<h1>404 - Page Not Found</h1>
</div>
<div v-else-if="frontmatter.layout === 'home'">
<h1>Welcome!</h1>
</div>
<article v-else>
<Content />
</article>
</main>
<footer>
<p>© 2024 My Site</p>
</footer>
</div>
</template>
```
## Runtime API
Access VitePress data in your theme:
```vue
<script setup>
import { useData, useRoute, useRouter } from 'vitepress'
// Page and site data
const {
site, // Site config (title, description, etc.)
theme, // Theme config
page, // Current page data
frontmatter, // Current page frontmatter
title, // Page title
description, // Page description
lang, // Current language
isDark, // Dark mode state
params // Dynamic route params
} = useData()
// Routing
const route = useRoute()
const router = useRouter()
// Navigate programmatically
const goToGuide = () => router.go('/guide/')
</script>
```
## Built-in Components
```vue
<script setup>
import { Content } from 'vitepress'
</script>
<template>
<!-- Renders markdown content -->
<Content />
<!-- Renders slot only on client (SSR-safe) -->
<ClientOnly>
<NonSSRComponent />
</ClientOnly>
</template>
```
## Extend Another Theme
Build on top of default theme or any other:
```ts
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// Your customizations
}
}
```
## Register Plugins and Components
```ts
// .vitepress/theme/index.ts
import Layout from './Layout.vue'
import GlobalComponent from './GlobalComponent.vue'
export default {
Layout,
enhanceApp({ app }) {
// Register global component
app.component('GlobalComponent', GlobalComponent)
// Register plugin
app.use(MyPlugin)
// Provide/inject
app.provide('key', value)
}
}
```
## Async enhanceApp
For plugins that need async initialization:
```ts
export default {
Layout,
async enhanceApp({ app }) {
if (!import.meta.env.SSR) {
// Client-only plugin
const plugin = await import('browser-only-plugin')
app.use(plugin.default)
}
}
}
```
## Theme-Aware Layout
Handle different page layouts:
```vue
<script setup>
import { useData } from 'vitepress'
import Home from './Home.vue'
import Doc from './Doc.vue'
import Page from './Page.vue'
import NotFound from './NotFound.vue'
const { page, frontmatter } = useData()
</script>
<template>
<NotFound v-if="page.isNotFound" />
<Home v-else-if="frontmatter.layout === 'home'" />
<Page v-else-if="frontmatter.layout === 'page'" />
<Doc v-else />
</template>
```
## Distributing a Theme
As npm package:
```ts
// my-theme/index.ts
import Layout from './Layout.vue'
export default { Layout }
// Export types for config
export type { ThemeConfig } from './types'
```
Consumer usage:
```ts
// .vitepress/theme/index.ts
import Theme from 'my-vitepress-theme'
export default Theme
// Or extend it
export default {
extends: Theme,
enhanceApp({ app }) {
// Additional customization
}
}
```
## Theme Config Types
For custom theme config types:
```ts
// .vitepress/config.ts
import { defineConfigWithTheme } from 'vitepress'
import type { ThemeConfig } from 'my-theme'
export default defineConfigWithTheme<ThemeConfig>({
themeConfig: {
// Type-checked theme config
}
})
```
## Key Points
- Theme must export `Layout` component
- `<Content />` renders the markdown content
- Use `useData()` to access page/site data
- `enhanceApp` runs on both server and client
- Check `import.meta.env.SSR` for client-only code
- Use `extends` to build on existing themes
<!--
Source references:
- https://vitepress.dev/guide/custom-theme
-->