963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
4.9 KiB
4.9 KiB
name, description
| name | description |
|---|---|
| vitepress-custom-themes | 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:
// .vitepress/theme/index.ts
import Layout from './Layout.vue';
export default {
Layout,
enhanceApp({ app, router, siteData }) {
// Register global components, plugins, etc.
},
};
Theme Interface
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:
<!-- .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:
<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
<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:
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme';
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
// Your customizations
},
};
Register Plugins and Components
// .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:
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:
<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:
// my-theme/index.ts
import Layout from './Layout.vue';
export default { Layout };
// Export types for config
export type { ThemeConfig } from './types';
Consumer usage:
// .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:
// .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
Layoutcomponent <Content />renders the markdown content- Use
useData()to access page/site data enhanceAppruns on both server and client- Check
import.meta.env.SSRfor client-only code - Use
extendsto build on existing themes