Create an XML Sitemap in Vue · Nuxt SEO

-
-
-
-

[1.4K](https://github.com/harlan-zw/nuxt-seo)

[Nuxt SEO on GitHub](https://github.com/harlan-zw/nuxt-seo)

Learn SEO

Master search optimization

Nuxt

 Vue

-
-
-
-
-
-
-

-
-
-
-
-
-
-

-
-
-

-
-
-
-
-
-
-
-
-
-
-

-
-
-

-
-
-
-
-
-
-
-
-

1.
2.
3.
4.
5.

# Create an XML Sitemap in Vue

Generate sitemaps for Vue SPAs using Vite plugins, server-side rendering, or build-time generation.

[![Harlan Wilton](https://avatars.githubusercontent.com/u/5326365?v=4)Harlan Wilton](https://x.com/harlan-zw)8 mins read Published Nov 3, 2024 Updated Jan 29, 2026

What you'll learn

- Sitemaps are limited to 50,000 URLs or 50MB. use sitemap index for larger sites
- Only include indexable pages. skip noindexed, redirected, or error pages
- Submit to Google Search Console and reference in robots.txt

The `sitemap.xml` file helps search engines discover your pages. Google considers sites "small" if they have [500 pages or fewer](https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview). you likely need a sitemap if you exceed this, have new sites with few backlinks, or update content frequently.

## [Static Sitemap](#static-sitemap)

For small sites under 100 pages, create a static sitemap in your public directory:

```
public/
  sitemap.xml
```

Add your URLs with proper formatting:

```
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://mysite.com/</loc>
    <lastmod>2026-01-03</lastmod>
  </url>
  <url>
    <loc>https://mysite.com/about</loc>
    <lastmod>2026-01-15</lastmod>
  </url>
</urlset>
```

Sitemaps are limited to [50,000 URLs or 50MB uncompressed](https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap). Use UTF-8 encoding and absolute URLs only.

## [Build-Time Generation with Vite](#build-time-generation-with-vite)

For most Vue projects, use [vite-plugin-sitemap](https://github.com/jbaubree/vite-plugin-sitemap) to auto-generate sitemaps during build:

vite.config.ts

```
import Sitemap from 'vite-plugin-sitemap'

export default defineConfig({
  plugins: [
    Sitemap({
      hostname: 'https://mysite.com',
      dynamicRoutes: [
        '/blog/post-1',
        '/blog/post-2'
      ]
    })
  ]
})
```

After running `npm build`, this generates `sitemap.xml` and `robots.txt` in your dist folder.

### [Vue Router Integration](#vue-router-integration)

If using Vue Router, generate routes from your router configuration:

vite.config.ts

```
import Sitemap from 'vite-plugin-sitemap'
import routes from './src/router/routes'

export default defineConfig({
  plugins: [
    Sitemap({
      hostname: 'https://mysite.com',
      dynamicRoutes: routes.map(route => route.path)
    })
  ]
})
```

Avoid hash-based routing (`/#/about`). [search engines can't process sitemap entries with hash links](https://dev.to/niickfraziier/building-seo-friendly-single-page-applications-spas-with-react-and-vue-44fa). Use history mode instead.

## [Server-Side Sitemap Generation](#server-side-sitemap-generation)

For sites with frequently changing content (e-commerce, blogs, news), generate sitemaps server-side:

Express

Vite

H3

```
import express from 'express'

const app = express()

app.get('/sitemap.xml', async (req, res) => {
  const pages = await fetchAllPages()

  const urls = pages.map(page => \`
  <url>
    <loc>https://mysite.com${page.path}</loc>
    <lastmod>${page.updatedAt}</lastmod>
  </url>\`).join('\n')

  const sitemap = \`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>\`

  res.type('application/xml').send(sitemap)
})
```

```
// server.js for Vite SSR
import express from 'express'

const app = express()

app.use(async (req, res, next) => {
  if (req.path === '/sitemap.xml') {
    const pages = await fetchAllPages()

    const urls = pages.map(page => \`
  <url>
    <loc>https://mysite.com${page.path}</loc>
    <lastmod>${page.updatedAt}</lastmod>
  </url>\`).join('\n')

    const sitemap = \`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>\`

    return res.type('application/xml').send(sitemap)
  }
  next()
})
```

```
import { defineEventHandler, setHeader } from 'h3'

export default defineEventHandler(async (event) => {
  if (event.path === '/sitemap.xml') {
    const pages = await fetchAllPages()

    const urls = pages.map(page => \`
  <url>
    <loc>https://mysite.com${page.path}</loc>
    <lastmod>${page.updatedAt}</lastmod>
  </url>\`).join('\n')

    const sitemap = \`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>\`

    setHeader(event, 'Content-Type', 'application/xml')
    return sitemap
  }
})
```

This works with any [Node.js](https://nodejs.org) server or SSR framework. For SPAs without SSR, use build-time generation instead. [client-side rendering makes sitemaps harder](https://medium.com/@ninapepite/spa-and-seo-b2735716dfc4) since you need to know all routes at build time.

## [XML Sitemap Tags](#xml-sitemap-tags)

A basic sitemap uses these elements:

```
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://mysite.com/page</loc>
    <lastmod>2026-01-03</lastmod>
  </url>
</urlset>
```

### [Required and Optional Tags](#required-and-optional-tags)

| Tag | Status | Google Uses? |
| --- | --- | --- |
| `<loc>` | Required | Yes, the page URL |
| `<lastmod>` | Recommended | Yes, [if consistently accurate](https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap) |
| `<changefreq>` | Skip | [No, Google ignores this](https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping) |
| `<priority>` | Skip | [No. Google ignores this](https://developers.google.com/search/blog/2014/10/best-practices-for-xml-sitemaps-rssatom) |

Google only uses `<lastmod>` if it matches reality. If your page changed 7 years ago but you claim it updated yesterday, [Google stops trusting your sitemap](https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview#sitemap-tags).

Don't bother with `<changefreq>` or `<priority>`. they increase file size without adding value.

## [Sitemap Index for Large Sites](#sitemap-index-for-large-sites)

If you exceed 50,000 URLs or 50MB, [split your sitemap into multiple files](https://developers.google.com/search/docs/crawling-indexing/sitemaps/large-sitemaps):

sitemap-index.xml

```
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://mysite.com/sitemap-products.xml</loc>
    <lastmod>2026-01-03</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://mysite.com/sitemap-blog.xml</loc>
    <lastmod>2026-01-10</lastmod>
  </sitemap>
</sitemapindex>
```

Submit the index file to Google Search Console. it will crawl all referenced sitemaps. [Maximize URLs per sitemap](https://developers.google.com/search/blog/2014/10/best-practices-for-xml-sitemaps-rssatom) rather than creating many small files.

## [Specialized Sitemaps](#specialized-sitemaps)

### [News Sitemap](#news-sitemap)

News publishers should create [separate news sitemaps](https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap) with articles from the last 2 days:

sitemap-news.xml

```
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
  <url>
    <loc>https://mysite.com/article</loc>
    <news:news>
      <news:publication>
        <news:name>Site Name</news:name>
        <news:language>en</news:language>
      </news:publication>
      <news:publication_date>2026-01-29T12:00:00+00:00</news:publication_date>
      <news:title>Article Title</news:title>
    </news:news>
  </url>
</urlset>
```

Remove articles older than 2 days to keep the sitemap fresh. Don't create new sitemaps daily. update the existing one.

### [Image Sitemap](#image-sitemap)

For galleries or image-heavy sites, add image metadata:

sitemap.xml

```
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  <url>
    <loc>https://mysite.com/page</loc>
    <image:image>
      <image:loc>https://mysite.com/image.jpg</image:loc>
      <image:title>Image Title</image:title>
    </image:image>
  </url>
</urlset>
```

[Image sitemaps help Google find images loaded via JavaScript](https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps) or not directly linked in HTML.

## [Submit to Google Search Console](#submit-to-google-search-console)

After generating your sitemap, [submit it to Google Search Console](https://support.google.com/webmasters/answer/7451001):

1. [Verify site ownership](https://seotesting.com/google-search-console/how-to-create-a-sitemap-for-google-search-console/) (DNS, HTML file upload, or Google Analytics)
2. Navigate to **Sitemaps** under **Indexing**
3. Enter your sitemap URL: `https://mysite.com/sitemap.xml`
4. Click **Submit**

Google processes the sitemap and reports status:

- **Success**. sitemap accepted, pages discovered
- **Pending**. processing in progress
- **Couldn't fetch**. URL wrong or blocked by robots.txt

Check the Sitemaps report for coverage stats, indexing errors, and discovered URLs.

### [Before Submitting](#before-submitting)

Validate your sitemap:

- XML syntax is valid (use an [online validator](https://www.xml-sitemaps.com/validate-xml-sitemap.html))
- All URLs return 200 status (not 404 or 500)
- URLs match exactly
- `<lastmod>` dates are accurate
- File uses UTF-8 encoding

### [Alternative Submission](#alternative-submission)

Reference your sitemap in `robots.txt`:

public/robots.txt

```
User-agent: *
Allow: /

Sitemap: https://mysite.com/sitemap.xml
```

Google discovers this automatically when

.
## [Using Nuxt?](#using-nuxt)

If you're using Nuxt, check out

 which handles much of this automatically.

---

On this page

- [Static Sitemap](#static-sitemap)
- [Build-Time Generation with Vite](#build-time-generation-with-vite)
- [Server-Side Sitemap Generation](#server-side-sitemap-generation)
- [XML Sitemap Tags](#xml-sitemap-tags)
- [Sitemap Index for Large Sites](#sitemap-index-for-large-sites)
- [Specialized Sitemaps](#specialized-sitemaps)
- [Submit to Google Search Console](#submit-to-google-search-console)
- [Using Nuxt?](#using-nuxt)

[GitHub](https://github.com/harlan-zw/nuxt-seo) [ Discord](https://discord.com/invite/275MBUBvgP)

###

-
-

Modules

-
-
-
-
-
-
-
-
-

###

-
-
-

###

Nuxt

-
-
-
-
-

Vue

-
-
-
-
-
-
-
-

###

-
-
-
-
-
-
-
-
-
-

Copyright © 2023-2026 Harlan Wilton - [MIT License](https://github.com/harlan-zw/nuxt-seo/blob/main/license) · [mdream](https://mdream.dev)