---
title: "VitePress SEO: Sitemaps, Meta Tags, and Search"
description: "Set up VitePress's built-in sitemap, per-page meta tags, and local search, then know when you actually need Nuxt Content instead."
canonical_url: "https://nuxtseo.com/learn-seo/vue/ssr-frameworks/vitepress"
last_updated: "2026-10-05"
---

> Index of every page on this site: [llms.txt](https://nuxtseo.com/llms.txt)

::key-takeaways
- [VitePress](https://vitepress.dev) pre-renders page content to HTML by default. Client-only components still depend on browser JavaScript
- Sitemap generation, per-page meta tags, and local search all ship built in, no extra packages needed for the basics
- It generates static pages. Use a server framework for request-time rendering. Protect private static files through host access controls
::

VitePress is a static site generator built on [Vite](https://vite.dev) and Vue 3, designed for documentation sites and content-heavy blogs. Page content renders to HTML at build time by default. Client-only components appear after browser JavaScript runs. Navigation after that first load behaves like an SPA.

## When to Use VitePress

VitePress fits these requirements:

- You're building technical documentation with minimal setup
- Your publishing workflow builds Markdown pages ahead of requests
- Page content can be public static files, with no request-time server logic
- You want bundled code highlighting and an optional local search index

Choose Nuxt Content when you need request-time rendering, Vue page routes alongside content, or integration with Nuxt modules. VitePress itself uses Vue SSR at build time. It does not provide a request-time application server.

This guide uses VitePress 1.6.4. Start with an [installed VitePress project](https://vuejs.github.io/vitepress/v1/guide/getting-started), then add the settings below to its existing config. Build with `pnpm exec vitepress build` and inspect `.vitepress/dist` before deployment.

## SEO Features

### Built-in Sitemap Generation

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

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

This generates `sitemap.xml` in `.vitepress/dist` during build. For `<lastmod>`{lang="html"} tags, enable `lastUpdated`. VitePress uses each page's Git timestamp or an explicit `lastUpdated` frontmatter date. A build needs that history or date to emit the value:

```ts [.vitepress/config.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:

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

This filter removes sitemap entries only. It does not unpublish, noindex, or protect built draft files. Exclude private drafts from the build or restrict access at the host.

### Build Hooks for Custom Sitemaps

For custom output, the [`buildEnd`](https://vuejs.github.io/vitepress/v1/reference/site-config#buildend) hook runs after page generation. It does not supply an application-specific `generatePageList` helper. Use native sitemap generation unless your project needs a separate generator with its own installed dependencies and page inventory.

## Meta Tags Configuration

### Site-Level Meta Tags

Add meta tags globally using the [`head` config](https://vuejs.github.io/vitepress/v1/reference/site-config):

```ts [.vitepress/config.ts]
export default {
  description: 'My documentation site',
  head: [
    ['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>`{lang="html"} tag, after VitePress's own built-in tags.

### Page-Level Meta Tags

Override meta tags per page using [frontmatter](https://vuejs.github.io/vitepress/v1/reference/frontmatter-config):

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

VitePress 1.6.4 merges site and page head entries and replaces matching meta tags. Use the `description` field for descriptions. Assign each dynamic property in one place so its value is clear, then inspect the generated HTML.

### Dynamic Meta Tags

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

```ts [.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 a build-only equivalent, use [`transformHead`](https://vuejs.github.io/vitepress/v1/reference/site-config#transformhead): it runs when generating production HTML. Check those tags in a build, rather than relying on the dev response.

## Built-in Search

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

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

No external service required; search runs in the browser against an indexed bundle built at compile time. If the local index becomes too large for your readers, evaluate [Algolia DocSearch](https://vuejs.github.io/vitepress/v1/reference/default-theme-search). It requires a configured index and search credentials:

```ts [.vitepress/config.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:

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

Enable this only when your host serves `/page.html` for requests to `/page` without redirecting back to the extension. The setting changes generated links and sitemap URLs. It does not configure host redirects. If both variants are accessible, choose a preferred form and align internal links, sitemap entries, and canonical tags with it.

## VitePress vs Nuxt Content

| Feature                       | VitePress                     | Nuxt Content                                             |
| ----------------------------- | ----------------------------- | -------------------------------------------------------- |
| **Page rendering**            | Vue SSR at build time         | Nuxt SSR or prerendering, according to app configuration |
| **Search**                    | Optional bundled local search | Content search APIs with an app-owned interface          |
| **Page routes**               | Markdown-based routes         | Content queried from Vue page routes                     |
| **Request-time server logic** | Requires a separate server    | Available in a server-deployed Nuxt app                  |

Choose by rendering and publishing requirements. Package downloads and unmatched build timings do not establish which option serves your readers better.

## Limitations

- **No request-time page renderer.** Page HTML is generated at build time. Client-side login cannot protect private content already published in those files.
- **Build-time content.** Rebuild when published page content changes. Browser components can still request data after hydration.
- **Hosting remains separate.** Configure redirects, access control, and clean URL support on the host.
- **Separate routing systems.** If you combine VitePress with an existing app, define which deployment owns each URL. A separate site can avoid overlapping routes.

Using Nuxt? [@nuxtjs/seo](https://nuxtseo.com/learn-seo/nuxt) integrates sitemap, robots.txt, structured data, and OG image modules. Configure your site URL and the features your pages need. Learn more about [Nuxt Content for documentation](https://content.nuxt.com/).

## Sitemap

See the full [sitemap](https://nuxtseo.com/sitemap.md) for all pages.
