Site Config
Site config is where you can define the global settings of the site. App config options define settings that apply to every VitePress site, regardless of what theme it is using. For example, the base directory or the title of the site.
Overview
Section titled “Overview”Config Resolution
Section titled “Config Resolution”The config file is always resolved from <root>/.vitepress/config.[ext], where <root> is your VitePress project root, and [ext] is one of the supported file extensions. TypeScript is supported out of the box. Supported extensions include .js, .ts, .mjs, and .mts.
It is recommended to use ES modules syntax in config files. The config file should default export an object:
export default {
// app level config options
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
...
}Dynamic (Async) Config
If you need to dynamically generate the config, you can also default export a function. For example:
import { defineConfig } from 'vitepress'
export default async () => {
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
return defineConfig({
// app level config options
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
// theme level config options
themeConfig: {
sidebar: [
...posts.map((post) => ({
text: post.name,
link: `/posts/${post.name}`
}))
]
}
})
}You can also use top-level await. For example:
import { defineConfig } from 'vitepress'
const posts = await (await fetch('https://my-cms.com/blog-posts')).json()
export default defineConfig({
// app level config options
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
// theme level config options
themeConfig: {
sidebar: [
...posts.map((post) => ({
text: post.name,
link: `/posts/${post.name}`
}))
]
}
})Config Intellisense
Section titled “Config Intellisense”Using the defineConfig helper will provide TypeScript-powered intellisense for config options. Assuming your IDE supports it, this should work in both JavaScript and TypeScript.
import { defineConfig } from 'vitepress'
export default defineConfig({
// ...
})Typed Theme Config
Section titled “Typed Theme Config”By default, defineConfig helper expects the theme config type from default theme:
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
// Type is `DefaultTheme.Config`
}
})If you use a custom theme and want type checks for the theme config, you'll need to use defineConfigWithTheme instead, and pass the config type for your custom theme via a generic argument:
import { defineConfigWithTheme } from 'vitepress'
import type { ThemeConfig } from 'your-theme'
export default defineConfigWithTheme<ThemeConfig>({
themeConfig: {
// Type is `ThemeConfig`
}
})Vite, Vue & Markdown Config
Section titled “Vite, Vue & Markdown Config”-
Vite
You can configure the underlying Vite instance using the vite option in your VitePress config. No need to create a separate Vite config file.
-
Vue
VitePress already includes the official Vue plugin for Vite (@vitejs/plugin-vue). You can configure its options using the vue option in your VitePress config.
-
Markdown
You can configure the underlying Markdown-It instance using the markdown option in your VitePress config.
Page-Level Overrides
Section titled “Page-Level Overrides”Some settings can be overridden for specific pages using frontmatter.
See Frontmatter Config for details.
Directory-Level Overrides
Section titled “Directory-Level Overrides”Some config settings can be overridden at the directory level, allowing all pages in that directory to share settings without needing to repeat them in the frontmatter of each page.
This is achieved by adding a file called config.ts (or .js, .mjs, or .mts) in the relevant directory. This file should export a config object using export default, similar to the main config file.
Nested directories inherit settings from their parent directory, with configuration overrides being merged accordingly.
The defineAdditionalConfig helper can be used to get TypeScript-powered intellisense for the available options, though as with defineConfig its use is optional.
For example, for a site with multiple languages we might want a different description for each language. We could add es/config.ts with the following content:
import { defineAdditionalConfig } from 'vitepress'
export default defineAdditionalConfig({
description: 'Generador de Sitios Estáticos desarrollado con Vite y Vue.'
})This description would then be used for all pages in the es directory.
Alternatively, when using the built-in i18n features, the settings for a locale directory can be overridden via the locales setting in the main configuration file. See Internationalization for details.
Site Metadata
Section titled “Site Metadata”- Type:
string - Default:
VitePress - Can be overridden per page via frontmatter or at the directory level
Title for the site. When using the default theme, this will be displayed in the nav bar.
It will also be used as the default suffix for all individual page titles, unless titleTemplate is defined. An individual page's final title will be the text content of its first <h1> header, combined with the global title as the suffix. For example with the following config and page content:
export default {
title: 'My Awesome Site'
}# HelloThe title of the page will be Hello | My Awesome Site.
titleTemplate
Section titled “titleTemplate”- Type:
string | boolean - Can be overridden per page via frontmatter or at the directory level
Allows customizing each page's title suffix or the entire title. For example:
export default {
title: 'My Awesome Site',
titleTemplate: 'Custom Suffix'
}# HelloThe title of the page will be Hello | Custom Suffix.
To completely customize how the title should be rendered, you can use the :title symbol in titleTemplate:
export default {
titleTemplate: ':title - Custom Suffix'
}Here :title will be replaced with the text inferred from the page's first <h1> header. The title of the previous example page will be Hello - Custom Suffix.
The option can be set to false to disable title suffixes.
description
Section titled “description”- Type:
string - Default:
A VitePress site - Can be overridden per page via frontmatter or at the directory level
Description for the site. This will render as a <meta> tag in the page HTML.
export default {
description: 'A VitePress site'
}- Type:
HeadConfig[] - Default:
[] - Can be appended per page via frontmatter or at the directory level
Additional elements to render in the <head> tag in the page HTML. The user-added tags are rendered before the closing head tag, after VitePress tags.
type HeadConfig =
| [string, Record<string, string>]
| [string, Record<string, string>, string]Head entries from the site config, locale config, directory-level config, frontmatter and transformHead are merged in that order. A later entry replaces an earlier one with the same key instead of being appended:
- Any element with an
idattribute is keyed by itsid. - A
metaelement without anidis keyed by its first attribute other thancontent(e.g.name,property,http-equiv) and that attribute's value.
Other elements are never deduplicated. To render multiple meta tags that would share a key, like several <meta name="author">, give each of them a unique id.
Example: Adding a favicon
Section titled “Example: Adding a favicon”export default {
head: [['link', { rel: 'icon', href: '/favicon.ico' }]]
} // put favicon.ico in public directory, if base is set, use /base/favicon.ico
/* Would render:
<link rel="icon" href="/favicon.ico">
*/Example: Adding Google Fonts
Section titled “Example: Adding Google Fonts”export default {
head: [
[
'link',
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' }
],
[
'link',
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' }
],
[
'link',
{ href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap', rel: 'stylesheet' }
]
]
}
/* Would render:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
*/Example: Registering a service worker
Section titled “Example: Registering a service worker”export default {
head: [
[
'script',
{ id: 'register-sw' },
`;(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
}
})()`
]
]
}
/* Would render:
<script id="register-sw">
;(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
}
})()
</script>
*/Example: Using Google Analytics
Section titled “Example: Using Google Analytics”export default {
head: [
[
'script',
{ async: '', src: 'https://www.googletagmanager.com/gtag/js?id=TAG_ID' }
],
[
'script',
{},
`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'TAG_ID');`
]
]
}
/* Would render:
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'TAG_ID');
</script>
*/- Type:
string - Default:
en-US - Can be overridden at the directory level
The lang attribute for the site. This will render as a <html lang="en-US"> tag in the page HTML.
export default {
lang: 'en-US'
}- Type:
'ltr' | 'rtl' | 'auto' - Default:
ltr - Can be overridden at the directory level
The text direction of the site. This will render as a <html dir="rtl"> tag in the page HTML, and the default theme mirrors its layout for right-to-left languages. It can also be overridden per page via frontmatter. See RTL Support.
export default {
dir: 'rtl'
}- Type:
string - Default:
/
The base URL the site will be deployed at. You will need to set this if you plan to deploy your site under a sub path, for example, GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/, then you should set base to '/bar/'. It should always start and end with a slash.
The one exception is './', which produces a relocatable build: pages reference everything relative to their own location, so the same output works from any sub path (IPFS gateways, archives) without rebuilding and stays browsable when opened directly from the file system.
The base is automatically prepended to all the URLs that start with / in other options, so you only need to specify it once.
export default {
base: '/base/'
}Can also be set per build with vitepress build --base /base/.
Routing
Section titled “Routing”cleanUrls
Section titled “cleanUrls”- Type:
boolean - Default:
false
When set to true, VitePress will remove the trailing .html from URLs. Also see Generating Clean URLs.
rewrites
Section titled “rewrites”- Type:
Record<string, string>
Defines custom directory <-> URL mappings. See Routing: Route Rewrites for more details.
export default {
rewrites: {
'source/:page': 'destination/:page'
}
}srcDir
Section titled “srcDir”- Type:
string - Default:
.
The directory where your markdown pages are stored, relative to project root. Also see Root and Source Directory.
export default {
srcDir: './src'
}srcExclude
Section titled “srcExclude”- Type:
string[] - Default:
undefined
A glob pattern for matching markdown files that should be excluded as source content.
export default {
srcExclude: ['**/README.md', '**/TODO.md']
}outDir
Section titled “outDir”- Type:
string - Default:
./.vitepress/dist
The build output location for the site, relative to project root.
export default {
outDir: '../public'
}assetsDir
Section titled “assetsDir”- Type:
string - Default:
assets
Specify the directory to nest generated assets under. The path should be inside outDir and is resolved relative to it.
export default {
assetsDir: 'static'
}assetsBase
Section titled “assetsBase”- Type:
string - Default:
undefined
URL prefix the generated assets (everything under assetsDir) are served from — typically a CDN. Must be an absolute URL, a protocol-relative URL, or a root-absolute path; a trailing slash is appended if missing.
export default {
base: '/',
assetsBase: 'https://cdn.example.com/'
// scripts, styles, fonts and imported images resolve to
// https://cdn.example.com/assets/*
}The emitted asset URL is assetsBase joined with the output-relative file path, so the CDN should mirror the layout of outDir (upload outDir/assets so it is reachable at <assetsBase>/assets/*). HTML pages, Markdown links, public files and hashmap.json stay on base.
When assetsBase points at another origin, VitePress adds crossorigin to the emitted script and preload tags — the CDN must send Access-Control-Allow-Origin for your site's origin (module scripts are always fetched in CORS mode).
Only production builds are affected. vitepress preview serves a root-absolute assetsBase (like /cdn/) from the local dist; an external one is requested from the real URL. Can also be set per build with vitepress build --assetsBase https://cdn.example.com/.
assetsShards
Section titled “assetsShards”- Type:
number - Default:
undefined
Spreads the generated assets over this many subdirectories of assetsDir, assets/0/ through assets/N-1/, instead of one flat directory. Use it when the host caps the number of files per directory; Netlify, for example, allows 54,000. Each page emits two JavaScript files, so a site with 60,000 pages needs at least three shards, plus some headroom because files are distributed by a hash of their name.
export default {
assetsShards: 4
}Shared chunks stay in assets/chunks/. A file's shard depends only on its name, so unchanged files keep their URL between builds. Only production builds are affected.
- Type:
{ include?: string[] }
Options for the generated icon styles. The build collects every iconify icon rendered during SSR. Names are fully qualified as collection:name, resolved against the @iconify-json/* packages declared in your project's dependencies.
Icons rendered only on the client — inside <ClientOnly>, or after hydration — are invisible to SSR collection. List them in include to force them into the stylesheet:
export default {
icons: {
include: ['mdi:home', 'simple-icons:discord']
}
}cacheDir
Section titled “cacheDir”- Type:
string - Default:
./.vitepress/cache
The directory for cache files, relative to project root. See also: cacheDir.
export default {
cacheDir: './.vitepress/.vite'
}ignoreDeadLinks
Section titled “ignoreDeadLinks”- Type:
boolean | 'localhostLinks' | (string | RegExp | ((link: string, source: string) => boolean))[] - Default:
false
When set to true, VitePress will not fail builds due to dead links.
When set to 'localhostLinks', the build will fail on dead links, but won't check localhost links.
export default {
ignoreDeadLinks: true
}It can also be an array of exact url string, regex patterns, or custom filter functions.
export default {
ignoreDeadLinks: [
// ignore exact url "/playground"
'/playground',
// ignore all localhost links
/^https?:\/\/localhost/,
// ignore all links include "/repl/""
/\/repl\//,
// custom function, ignore all links include "ignore"
(url) => {
return url.toLowerCase().includes('ignore')
}
]
}mpa experimental
Section titled “mpa ”- Type:
boolean - Default:
false
When set to true, the production app will be built in MPA Mode. MPA mode ships 0kb JavaScript by default, at the cost of disabling client-side navigation and requires explicit opt-in for interactivity.
Theming
Section titled “Theming”appearance
Section titled “appearance”- Type:
boolean | 'dark' | 'force-dark' | 'force-auto' | import('@vueuse/core').UseDarkOptions - Default:
true
Whether to enable dark mode (by adding the .dark class to the <html> element).
- If the option is set to
true, the default theme will be determined by the user's preferred color scheme. - If the option is set to
dark, the theme will be dark by default, unless the user manually toggles it. - If the option is set to
false, users will not be able to toggle the theme. - If the option is set to
'force-dark', the theme will always be dark and users will not be able to toggle it. - If the option is set to
'force-auto', the theme will always be determined by the user's preferred color scheme and users will not be able to toggle it.
This option injects an inline script that restores users settings from local storage using the vitepress-theme-appearance key. This ensures the .dark class is applied before the page is rendered to avoid flickering.
appearance.initialValue can only be 'dark' | undefined. Refs or getters are not supported.
lastUpdated
Section titled “lastUpdated”- Type:
boolean - Default:
false
Whether to get the last updated timestamp for each page using Git. The timestamp will be included in each page's page data, accessible via useData.
When using the default theme, enabling this option will display each page's last updated time. You can customize the text via themeConfig.lastUpdated.text option.
Customization
Section titled “Customization”markdown
Section titled “markdown”- Type:
MarkdownOption
Configure Markdown parser options. VitePress uses Markdown-it as the parser, and Shiki to highlight language syntax. Inside this option, you may pass various Markdown related options to fit your needs.
export default {
markdown: {...}
}Check the type declaration and jsdocs for all the options available.
Set markdown.headers to true or pass @mdit-vue/plugin-headers options to collect headings into useData().page.headers. This option is disabled by default.
- Type:
import('vite').UserConfig
Pass raw Vite Config to internal Vite dev server / bundler.
export default {
vite: {
// Vite config options
}
}- Type:
import('@vitejs/plugin-vue').Options
Pass raw @vitejs/plugin-vue options to the internal plugin instance.
export default {
vue: {
// @vitejs/plugin-vue options
}
}Build Hooks
Section titled “Build Hooks”VitePress build hooks allow you to add new functionality and behaviors to your website:
- Sitemap
- Search Indexing
- PWA
- Teleports
buildEnd
Section titled “buildEnd”- Type:
(siteConfig: SiteConfig) => Awaitable<void>
buildEnd is a build CLI hook, it will run after build (SSG) finish but before VitePress CLI process exits.
export default {
async buildEnd(siteConfig) {
// ...
}
}postRender
Section titled “postRender”- Type:
(context: SSGContext) => Awaitable<SSGContext | void>
postRender is a build hook, called when SSG rendering is done. It will allow you to handle the teleports content during SSG.
export default {
async postRender(context) {
// ...
}
}interface SSGContext {
content: string
teleports?: Record<string, string>
vpIcons: Set<string>
[key: string]: any
}transformHead
Section titled “transformHead”- Type:
(context: TransformContext) => Awaitable<HeadConfig[]>
transformHead is a build hook to add extra tags to the <head> of each page. It allows you to add head entries that cannot be statically added to your VitePress config. You only need to return extra entries, they will be merged automatically with the existing ones.
export default {
async transformHead(context) {
// ...
}
}interface TransformContext {
page: string // e.g. index.md (relative to srcDir)
assets: string[] // all non-js/css assets as fully resolved public URL
siteConfig: SiteConfig
siteData: SiteData
pageData: PageData
title: string
description: string
head: HeadConfig[]
content: string
}This hook is only called when performing a build, it is not called during dev.
The extra tags will be added to the static HTML files generated by the build. They will not be updated during client-side navigation.
In many cases, using the transformPageData hook is a cleaner solution. That hook will also be applied to both client-side navigation and during dev. But if generating the head tags is computationally expensive then transformHead will avoid that overhead during dev.
Example: Adding og:image meta
Section titled “Example: Adding og:image meta”export default {
async transformHead(context) {
if (context.page === '404.md') {
return
}
// The implementation details of `generatePageImage` would depend
// on your requirements. Here we assume it generates a suitable
// image for each page and returns the image URL.
const imageUrl = await generatePageImage(context)
return [[
'meta',
{ name: 'og:image', content: imageUrl }
]]
}
}Here we're assuming that the image URL is dynamic and time-consuming to generate. Using transformHead avoids that overhead during development.
For simpler cases, it may be possible to use the head setting in frontmatter, or transformPageData.
transformHtml
Section titled “transformHtml”- Type:
(code: string, id: string, context: TransformContext) => Awaitable<string | void>
transformHtml is a build hook to transform the content of each page before saving to disk.
export default {
async transformHtml(code, id, context) {
// ...
}
}transformPageData
Section titled “transformPageData”- Type:
(pageData: PageData, context: TransformPageContext) => Awaitable<Partial<PageData> | { [key: string]: any } | void>
transformPageData is a hook to transform the pageData of each page. You can directly mutate pageData or return changed values which will be merged into the page data.
export default {
async transformPageData(pageData, { siteConfig }) {
pageData.contributors = await getPageContributors(pageData.relativePath)
}
// or return data to be merged
async transformPageData(pageData, { siteConfig }) {
return {
contributors: await getPageContributors(pageData.relativePath)
}
}
}interface TransformPageContext {
siteConfig: SiteConfig
}Example: Adding a <meta name="og:title">
Section titled “Example: Adding a <meta name="og:title">”export default {
transformPageData(pageData) {
const title = pageData.frontmatter.layout === 'home'
? 'VitePress'
: `${pageData.title} | VitePress`
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'meta',
{ name: 'og:title', content: title }
])
}
}Example: Adding a canonical URL <link>
Section titled “Example: Adding a canonical URL <link>”export default {
transformPageData(pageData) {
const canonicalUrl = `https://example.com/${pageData.relativePath}`
.replace(/index\.md$/, '')
.replace(/\.md$/, '.html')
pageData.frontmatter.head ??= []
pageData.frontmatter.head.push([
'link',
{ rel: 'canonical', href: canonicalUrl }
])
}
}