Sitemap for dynamic routes in NuxtJS
Tags: vue, nuxt, sitemap, seo
Published: 2020-10-13 12:35:06

# Motivation
As for the date of writing this post it is impossible to dynamically generate `sitemap.xml` in NuxtJS for dynamic routes using [@nuxtjs/sitemap](https://github.com/nuxt-community/sitemap-module).

As the [documentation](https://github.com/nuxt-community/sitemap-module#setup-a-sitemap) for the module says, you have manually type all your dynamic routes into the `routes` array of `sitemap` object in `nuxt.config.js`.

```js
// nuxt.config.js
{
  sitemap: {
    hostname: 'https://example.com',
    gzip: true,
    exclude: [
      '/secret',
      '/admin/**'
    ],
    routes: [
      'dynamic-route-one',
      'dynamic-route-two',
    ]
  }
}
``` 

There is also another way, where you have to create a script that will make all the API calls to your backend server then will generate an array of routes that the `sitemap` object in the `nuxt.config.js` will then consume. More on that you can search for yourself or take a look at this [article](https://elision.design/en/blog/dynamic-routes-in-sitemap-with-nuxt-js).

But I was very curious if there is another way, because it can be a pain the in the ass to type in all the dynamic routes manually or to make a lot of the API calls.

# Disclaimer
After reading this post you might think that this way of generating 
sitemap for dynamic routes is a bit hacky. And you are absolutely true. There is only one official way of doing it. Use at your own risk.

# Watered down theory
Since [Nuxt v2.13](https://nuxtjs.org/api/configuration-generate/#crawler) a new option in `nuxt.config.js` is available:

```
// nuxt.config.js
export default {
  generate: {
    crawler: false // default - true
  }
}
```
A quote from the documentation:

>As of Nuxt >= v2.13 Nuxt.js comes with a crawler installed that will crawl your relative links and generate your dynamic links based on these links. If you want to disable this feature you can set the value to false.

I have created a [repository](https://github.com/andynoir/article-nuxt-dynamic-sitemap) which has both dynamic and static routes and if we run `npm run generate` in the terminal we can see our dynamic routes being generated as individual html files:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5dcdcpuo40vvjkff6dwk.png)

But also you can see that the path to the dynamic route being printed out into the terminal, eg. - <code>Generated route **"/users/10"**</code>. So if it is printed out, maybe it is stored somewhere and we can obtain it or we can capture it while it is being printed out and we can store it.

# Coding part
Lets quickly install [@nuxtjs/sitemap](https://github.com/nuxt-community/sitemap-module) module by typing in the terminal:

`npm install @nuxtjs/sitemap`
or
`yarn add @nuxtjs/sitemap`

then add it to `nuxt.config.js` like this:
```
// nuxt.config.js
modules: [
    // your other modules
    '@nuxtjs/sitemap'
  ],
```
and configure it like this:
```
// nuxt.config.js
sitemap: {
    hostname: 'https://my-host.com',
    gzip: true,
    exclude: [
      '/exclude-one',
      '/exclude-two'
    ],
    defaults: {
      changefreq: 'daily',
      priority: 1,
      lastmod: new Date()
    }
  },
```

By running `npm run generate` again the in terminal we can ensure that dynamic routes are being generated but not being added to the `sitemap.xml`

```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" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1" xmlns:xhtml="http://www.w3.org/1999/xhtml">
   <url>
      <loc>https://my-host.com/albums</loc>
      <lastmod>2020-10-13T11:19:36.882Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/todos</loc>
      <lastmod>2020-10-13T11:19:36.882Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users</loc>
      <lastmod>2020-10-13T11:19:36.882Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/</loc>
      <lastmod>2020-10-13T11:19:36.882Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
</urlset>
``` 
In order to obtain those generated routes paths we will use NuxtJS [hooks](https://nuxtjs.org/api/internals-generator) which can be used in the NuxtJS [modules](https://nuxtjs.org/api/internals).

To create a module just make a `modules` folder in your project directory with a file inside called `generator.ts`

> Note: I am using TypeScript here, if you are using vanilla JavaScript it should be `generator.js`

Register your `generator.ts` in the `nuxt.config.js`

```js
// nuxt.config.js
 buildModules: [
    // other build modules
    '@/modules/generator'
  ],
```

Inside the `generator.ts` paste the following code and let's reflect on it a little bit.

```js
import { Module } from '@nuxt/types'

const generator: Module = function () {
  this.nuxt.hook('generate:done', async (context: any) => {
    const routesToExclude: string[] =
    process.env.NUXT_ENV_EXCLUDE_ROUTES
      ? process.env.NUXT_ENV_EXCLUDE_ROUTES.split(',') : []
    const allRoutes: string[] = await Array.from(context.generatedRoutes)
    const routes: string[] = await allRoutes.filter((route: string) => !routesToExclude.includes(route))
    this.nuxt.options.sitemap.routes = await [...routes]
  })
}

export default generator
```

1) We have defined a `generator` function that is exported and will be injected into NuxtJS.
2) We have subscribed to a `generate:done` hook and upon hook completion the code inside the function will be executed.
3) If you take a look [here](https://github.com/nuxt/nuxt.js/blob/819afcdeb447b73b675cfc654f641fc2df5894f9/packages/generator/src/generator.js#L53) you will see that a certain context will be returned by the hook. If you `console.log` that context inside our module, you will see a `generatedRoutes` [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
4) Inside `routesToExclude` I use a ternary operator to ensure that I do have some data in my `NUXT_ENV_EXCLUDE_ROUTES` environment variable:
```
// .env
NUXT_ENV_EXCLUDE_ROUTES = '/exclude-one,/exclude-two'
```
Then I divide my string into substrings to become an array of strings by using `.split` method.
5) Inside `allRoutes` I transform Set into an Array by using `Array.from` method.
6) I filter out all the routes I want to exclude by using `filter` method in `routes`
7) And lastly i spread my filtered routes into the `routes` property of the `sitemap` object: `this.nuxt.options.sitemap.routes = await [...routes]`

Now if you run `npm run generate` again you will see dynamic routes in the `sitemap.xml`

```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" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1" xmlns:xhtml="http://www.w3.org/1999/xhtml">
   <url>
      <loc>https://my-host.com/albums</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/todos</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/1</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/2</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/3</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/4</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/5</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/6</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/7</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/8</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/9</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
   <url>
      <loc>https://my-host.com/users/10</loc>
      <lastmod>2020-10-13T12:09:44.775Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>1.0</priority>
   </url>
</urlset>
```

# For those who are not familiar with TypeScript
Leave a comment below or DM me, i will try to help you out.

# Links
Live preview can be found here - https://andynoir.github.io/article-nuxt-dynamic-sitemap/
Sitemap here https://andynoir.github.io/article-nuxt-dynamic-sitemap/sitemap.xml
GitHub repo here - https://github.com/andynoir/article-nuxt-dynamic-sitemap