---
title: "Create an XML Sitemap in Vue"
description: "Generate sitemaps for Vue SPAs using Vite plugins, server-side rendering, or build-time generation."
canonical_url: "https://nuxtseo.com/learn-seo/vue/controlling-crawlers/sitemaps"
last_updated: "2026-01-29"
---

<key-takeaways>

- 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

</key-takeaways>

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

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

```dir
public/
  sitemap.xml
```

Add your URLs with proper formatting:

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

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

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

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

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

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

<code-group>

```ts [Express]
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)
})
```

```ts [Vite]
// 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()
})
```

```ts [H3]
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
  }
})
```

</code-group>

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

A basic sitemap uses these elements:

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

<table>
<thead>
  <tr>
    <th>
      Tag
    </th>
    
    <th>
      Status
    </th>
    
    <th>
      Google Uses?
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          loc
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Required
    </td>
    
    <td>
      Yes, the page URL
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          lastmod
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Recommended
    </td>
    
    <td>
      Yes, <a href="https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap" rel="nofollow">
        if consistently accurate
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          changefreq
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Skip
    </td>
    
    <td>
      <a href="https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping" rel="nofollow">
        No, Google ignores this
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          priority
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Skip
    </td>
    
    <td>
      <a href="https://developers.google.com/search/blog/2014/10/best-practices-for-xml-sitemaps-rssatom" rel="nofollow">
        No. Google ignores this
      </a>
    </td>
  </tr>
</tbody>
</table>

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

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):

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

### 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:

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

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

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

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

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 [canonical URLs](/learn-seo/vue/controlling-crawlers/canonical-urls) exactly
- `<lastmod>` dates are accurate
- File uses UTF-8 encoding

### Alternative Submission

Reference your sitemap in `robots.txt`:

```txt [public/robots.txt]
User-agent: *
Allow: /

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

Google discovers this automatically when [crawling your robots.txt](/learn-seo/vue/controlling-crawlers/robots-txt).

## Using Nuxt?

If you're using Nuxt, check out [Nuxt SEO](/docs/nuxt-seo/getting-started/introduction) which handles much of this automatically.

[Learn more about sitemaps in Nuxt →](/learn-seo/nuxt/controlling-crawlers/sitemaps)
