VitePress SEO Guide

VitePress offers built-in SEO features like sitemap generation, meta tags, and fast static site generation for documentation and blogs.
Harlan WiltonHarlan Wilton7 mins read Published
What you'll learn
  • VitePress is ideal for documentation and blogs—fast static generation with built-in search
  • Native sitemap generation with sitemap.hostname config option
  • Static-only (no SSR)—use Nuxt Content for dynamic features or authentication

VitePress is a static site generator built on Vite and Vue 3, designed for documentation sites and content-heavy blogs. The initial visit serves pre-rendered HTML for fast loading and optimal SEO, while subsequent navigation behaves like an SPA.

When to Use VitePress

VitePress beats Nuxt Content when:

  • Building technical documentation with minimal setup
  • Speed and simplicity matter more than flexibility
  • Content is 100% static (no authentication, no dynamic server features)
  • You want built-in search and code highlighting without configuration

VitePress has 395,965 weekly npm downloads vs Nuxt Content's 74,690. Choose Nuxt Content when you need SSR, complex routing with non-markdown pages, or deeper integration with Nuxt modules.

SEO Features

Automatic Static HTML

VitePress pre-renders pages at build time, generating static HTML files. This approach delivers excellent SEO while maintaining SPA benefits for user experience.

On PageSpeed Insights, VitePress sites achieve near-perfect performance scores even on low-end mobile devices—Google's Core Web Vitals directly impact rankings.

Built-in Sitemap Generation

VitePress includes native sitemap support. Enable it in .vitepress/config.ts:

.vitepress/config.ts
export default {
  sitemap: {
    hostname: 'https://mysite.com'
  }
}

This generates sitemap.xml in .vitepress/dist during build. For <lastmod> tags, enable the lastUpdated option:

.vitepress/config.ts
export default {
  sitemap: {
    hostname: 'https://mysite.com'
  },
  lastUpdated: true
}

VitePress uses the sitemap module under the hood. Pass any SitemapStream options directly to the sitemap config.

Custom Sitemap Items

Use transformItems to modify sitemap entries before writing:

.vitepress/config.ts
export default {
  sitemap: {
    hostname: 'https://mysite.com',
    transformItems: (items) => {
      // Filter out draft pages
      return items.filter(item => !item.url.includes('/drafts/'))
    }
  }
}

Alternative: Build Hooks

For advanced use cases, use VitePress build hooks like buildEnd to generate custom sitemaps:

.vitepress/config.ts
import { writeFileSync } from 'node:fs'
import { SitemapStream, streamToPromise } from 'sitemap'

export default {
  async buildEnd(siteConfig) {
    const sitemap = new SitemapStream({ hostname: 'https://mysite.com' })

    const pages = await generatePageList(siteConfig)
    pages.forEach(page => sitemap.write(page))

    sitemap.end()
    const data = await streamToPromise(sitemap)
    writeFileSync('.vitepress/dist/sitemap.xml', data.toString())
  }
}

Meta Tags Configuration

Site-Level Meta Tags

Add meta tags globally using the head config:

.vitepress/config.ts
export default {
  head: [
    ['meta', { name: 'description', content: 'My documentation site' }],
    ['meta', { property: 'og:type', content: 'website' }],
    ['meta', { property: 'og:image', content: 'https://mysite.com/og.png' }],
    ['link', { rel: 'icon', href: '/favicon.ico' }]
  ]
}

User-added tags render before the closing </head> tag, after VitePress's built-in tags.

Page-Level Meta Tags

Override meta tags per page using frontmatter:

---
head:
  -
    - meta
    - name: description
      content: Custom description for this page
  -
    - meta
    - property: og:title
      content: Custom OG Title
  -
    - meta
    - name: keywords
      content: vitepress, seo, vue
---

Frontmatter meta tags append after site-level tags. Note that setting OG tags globally and overriding per-page can cause duplicate tags in HTML output.

Dynamic Meta Tags

For dynamic meta tags, use transform hooks:

.vitepress/config.ts
export default {
  transformPageData(pageData) {
    pageData.frontmatter.head ??= []
    pageData.frontmatter.head.push([
      'meta',
      {
        property: 'og:title',
        content: pageData.frontmatter.layout === 'home'
          ? 'Homepage - My Site'
          : pageData.title
      }
    ])
  }
}

transformPageData runs during both dev and build. For build-only transforms, use transformHead, but note it doesn't run during development.

VitePress includes fuzzy full-text search using minisearch. Enable local search in .vitepress/config.ts:

.vitepress/config.ts
export default {
  themeConfig: {
    search: {
      provider: 'local'
    }
  }
}

No external service required—search runs in-browser using an indexed bundle.

Algolia DocSearch

For larger sites, integrate Algolia DocSearch:

.vitepress/config.ts
export default {
  themeConfig: {
    search: {
      provider: 'algolia',
      options: {
        appId: 'YOUR_APP_ID',
        apiKey: 'YOUR_API_KEY',
        indexName: 'YOUR_INDEX_NAME'
      }
    }
  }
}

Algolia crawls your site and provides advanced search with typo tolerance and result ranking.

Clean URLs

VitePress supports clean URLs without .html extensions. Enable them in config:

.vitepress/config.ts
export default {
  cleanUrls: true
}

Clean URLs avoid duplicate content issues where /page and /page.html are treated as separate URLs. This matters for sitemaps and canonical tags—search engines mark mismatched URLs as duplicate content.

VitePress vs Nuxt Content

FeatureVitePressNuxt Content
Build SpeedBlazing fast with ViteSlower with larger sites
Use CaseDocumentation, blogs, static contentFull web apps with markdown
SSRStatic-onlyFull SSR + SSG
SearchBuilt-in local searchRequires integration
Learning CurveSimpler, minimalisticSteeper, feature-rich
Page DirectoryMarkdown onlyMixes markdown + Vue pages (with friction)
EcosystemGrowing, documentation-focusedLarger, mature module system
AuthenticationDoesn't fit SSG modelSupported with SSR

VitePress excels for performance-critical static sites where all content is known at build time. Nuxt Content handles complex routing, dynamic content, and SSR requirements better.

Limitations

Using Nuxt?

Nuxt offers deeper SEO integration through @nuxtjs/seo, which handles sitemaps, robots.txt, structured data, and OG images with zero configuration.

Learn more about Nuxt Content for documentation →