---
title: "VitePress SEO: Sitemaps, Meta Tags, and Search · Nuxt SEO"
canonical_url: "https://nuxtseo.com/learn-seo/vue/ssr-frameworks/vitepress"
last_updated: "2026-07-16T12:00:00.000Z"
meta:
  author: "Harlan Wilton"
  description: "Set up VitePress's built-in sitemap, per-page meta tags, and local search, then know when you actually need Nuxt Content instead."
  "og:description": "Set up VitePress's built-in sitemap, per-page meta tags, and local search, then know when you actually need Nuxt Content instead."
  "og:title": "VitePress SEO: Sitemaps, Meta Tags, and Search · Nuxt SEO"
---

Nuxt SEO on GitHub

# **VitePress SEO: Sitemaps, Meta Tags, and Search**

Set up VitePress's built-in sitemap, per-page meta tags, and local search, then know when you actually need Nuxt Content instead.

[Harlan Wilton](https://x.com/harlan-zw)7 mins read Published **Dec 17, 2025** Updated **Jul 16, 2026**

**What you'll learn**

- [**~~VitePress~~**](https://vitepress.dev) pre-renders every page to static HTML at build time, so search engines get full content with no rendering delay
- Sitemap generation, per-page meta tags, and local search all ship built in, no extra packages needed for the basics
- It's static-only: no auth, no per-request data. Reach for Nuxt Content when you need SSR or dynamic pages

VitePress is a static site generator built on [**~~Vite~~**](https://vite.dev) and Vue 3, designed for documentation sites and content-heavy blogs. Every page renders to HTML at build time, so crawlers see complete content on the first request. Navigation after that first load behaves like an SPA.

## When to Use VitePress

VitePress beats Nuxt Content when:

- You're building technical documentation with minimal setup
- Speed and simplicity matter more than flexibility
- Content is 100% static: no authentication, no per-request server logic
- You want built-in search and code highlighting without configuration

Choose Nuxt Content instead when you need SSR, routing that mixes markdown with non-markdown pages, or deeper integration with the Nuxt module ecosystem.

## SEO Features

### Built-in Sitemap Generation

VitePress includes [**~~native sitemap support~~**](https://vitepress.dev/guide/sitemap-generation), powered by the [`**sitemap**`](https://www.npmjs.com/package/sitemap) [**~~npm~~**](https://npmjs.com) package. Enable it in `**.vitepress/config.ts**`:

.vitepress/config.ts

```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

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

Any [`**SitemapStream**`](https://www.npmjs.com/package/sitemap) option can be passed straight into the `**sitemap**` config.

### Custom Sitemap Items

Use `**transformItems**` to modify sitemap entries before they're written:

.vitepress/config.ts

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

### Build Hooks for Custom Sitemaps

For anything `**transformItems**` can't handle, use the [`**buildEnd**`](https://vitepress.dev/reference/site-config#buildend) hook, which runs after the SSG build finishes but before the CLI process exits:

.vitepress/config.ts

```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~~**](https://vitepress.dev/reference/site-config):

.vitepress/config.ts

```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 own built-in tags.

### Page-Level Meta Tags

Override meta tags per page using [**~~frontmatter~~**](https://vitepress.dev/reference/frontmatter-config):

```yaml
---
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 tags append after site-level tags rather than replacing them. Set the same property at both levels and it renders twice in the HTML output, which [**~~has caused duplicate OG tags~~**](https://github.com/vuejs/vitepress/issues/975) for users who set `**og:title**` globally and again per page. Set dynamic properties like `**og:title**` only in frontmatter, or only globally, not both.

### Dynamic Meta Tags

For meta tags computed from page data, use [`**transformPageData**`](https://vitepress.dev/reference/site-config#transformpagedata):

.vitepress/config.ts

```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 a build-only equivalent, use [`**transformHead**`](https://vitepress.dev/reference/site-config#transformhead): it doesn't run during development, so it won't slow down the dev server.

## Built-in Search

VitePress supports [**~~fuzzy full-text search~~**](https://vitepress.dev/reference/default-theme-search) using an in-browser index, powered by [**~~minisearch~~**](https://github.com/lucaong/minisearch/). Enable it in `**.vitepress/config.ts**`:

.vitepress/config.ts

```ts
export default {
  themeConfig: {
    search: {
      provider: 'local'
    }
  }
}
```

No external service required; search runs in the browser against an indexed bundle built at compile time. For larger sites, integrate [**~~Algolia DocSearch~~**](https://vitepress.dev/reference/default-theme-search) instead, which crawls your site and indexes it separately, worth it once local search starts feeling slow on a large docs set:

.vitepress/config.ts

```ts
export default {
  themeConfig: {
    search: {
      provider: 'algolia',
      options: {
        appId: 'YOUR_APP_ID',
        apiKey: 'YOUR_API_KEY',
        indexName: 'YOUR_INDEX_NAME'
      }
    }
  }
}
```

## Clean URLs

VitePress can serve URLs without the `**.html**` extension:

.vitepress/config.ts

```ts
export default {
  cleanUrls: true
}
```

Turn this on. Without it, `**/page**` and `**/page.html**` can both resolve, and if anything links to both forms, search engines can index them as separate, duplicate URLs. That matters for sitemaps and canonical tags: pick one form and make sure every internal link, sitemap entry, and canonical tag uses it.

## VitePress vs Nuxt Content

| **Feature** | **VitePress** | **Nuxt Content** |
| --- | --- | --- |
| **Weekly npm downloads** | \~ [**~~510K~~**](https://www.npmjs.com/package/vitepress) | \~ [**~~110K~~**](https://www.npmjs.com/package/@nuxt/content) |
| **Build speed** | Fast, Vite-native | Slower on large sites |
| **Use case** | Documentation, blogs, static content | Full web apps with markdown |
| **SSR** | Static-only | Full SSR + SSG |
| **Search** | Built-in local search | Requires integration |
| **Learning curve** | Simple, minimal config | More to learn, more it can do |
| **Page directory** | Markdown only | Mixes markdown + Vue pages |
| **Authentication** | [**~~Doesn't fit the static-generation model~~**](https://github.com/vuejs/vitepress/discussions/548) | Supported with SSR |

VitePress wins on performance-critical static sites where all content is known at build time. Nuxt Content is the better fit once you need complex routing, dynamic content, or SSR.

## Limitations

- **No SSR.** All content generates at build time; dynamic features like user authentication [**~~don't fit the model~~**](https://github.com/vuejs/vitepress/discussions/548)
- **Static content only.** Pages that need server-side data fetching need Nuxt or a custom Vite SSR setup
- **Smaller ecosystem.** Fewer plugins than Nuxt, though it's growing
- **Mixed routing is friction, not a feature.** Running VitePress inside an existing Nuxt project [**~~tends to produce routing and layout conflicts~~**](https://github.com/vuejs/vitepress/discussions/3785); running it as a separate site avoids the problem entirely

Using Nuxt? [**~~@nuxtjs/seo~~**](https://nuxtseo.com/learn-seo/nuxt) offers deeper SEO integration, handling sitemaps, robots.txt, structured data, and OG images with zero configuration. Learn more about [**~~Nuxt Content for documentation~~**](https://content.nuxt.com/).

[**The 2026 SEO Checklist for Nuxt & Vue ** Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.](https://nuxtseo.com/learn-seo/checklist) [Haven't launched yet? Start with the **Pre-Launch Warmup**](https://nuxtseo.com/learn-seo/pre-launch-warmup)

---

### **Related **

[**Vue SSR Frameworks**](https://nuxtseo.com/learn-seo/vue/ssr-frameworks)

[**Vite SSR**](https://nuxtseo.com/learn-seo/vue/ssr-frameworks/vite-ssr)

[**XML Sitemaps**](https://nuxtseo.com/learn-seo/vue/controlling-crawlers/sitemaps)

[**Custom Vite SSR** Build Vue SSR by hand with Vite: routing, data fetching, and the meta-tag and hydration mistakes that quietly break SEO.](https://nuxtseo.com/learn-seo/vue/ssr-frameworks/vite-ssr) [**Launch & Listen** Submit your sitemap to Search Console, verify indexing, and track rankings and AI visibility after your Vue site goes live.](https://nuxtseo.com/learn-seo/vue/launch-and-listen)

**On this page**

- [When to Use VitePress](#when-to-use-vitepress)
- [SEO Features](#seo-features)
- [Meta Tags Configuration](#meta-tags-configuration)
- [Built-in Search](#built-in-search)
- [Clean URLs](#clean-urls)
- [VitePress vs Nuxt Content](#vitepress-vs-nuxt-content)
- [Limitations](#limitations)