Sitemap: What is and how to generate it for a Next.js App
Tags: nextjs, react, seo, sitemap
Published: 2021-03-27 17:36:55

Trying to improve my personal site SEO I end up with the need to generate dynamically a sitemap, but first of all...

### What is a Sitemap?

A **sitemap** is a blueprint of your website that help search engines find, crawl and index all of your website's content. Yep, I saved you a google search 😉

The sitemap is located on `/sitemap.xml` and looks like

```xml
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://santosnicolas.com/404</loc>
  </url>
  <url>
	  <loc>https://santosnicolas.com/blog</loc>
  </url>
  <url>
  	<loc>https://santosnicolas.com</loc>
  </url>
  <url>
  	<loc>https://santosnicolas.com/notes</loc>
  </url>
  <url>
  	<loc>https://santosnicolas.com/notes/whatever-post-title</loc>
  </url>
</urlset>
```

### How I generate the sitemap on Next.js

We basically need to add

```xml
<url>
	<loc>${routePage}</loc>
</url>
```

For every page that we had on our application.

Because of this, we need to get all our page routes or at least the ones that are public. This is an easy task with [globby](https://github.com/sindresorhus/globby), this lib allows us to get the name of the files based on regex URL on our folder structure.

```js
const globby = require("globby")

;(async () => {
  // Take all the pages except for _app.tsx and _document.tsx
  const pagesPaths = await globby(["pages/*.tsx", "!pages/_*.tsx"])

  console.log(pagesPaths)
  //=> ['index.tsx', 'blog.tsx', 'notes.tsx']
})()
```

With `fs ` and [prettier](http://prettier.io/) we can format and write our generated file(`sitemap.xml`) to located in the public folder.

```js
// generateSitemap.js
const fs = require("fs")
const globby = require("globby")
const prettier = require("prettier")

;(async () => {
  console.info("Generating Sitemap 🗺")
  const prettierConfig = await prettier.resolveConfig("./.prettierrc.js")
  const pages = await globby(["pages/*.tsx", "!pages/_*.tsx"])

  const sitemap = `
        <?xml version="1.0" encoding="UTF-8"?>
        <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
            ${pages
              .map((page) => {
                const path = page
                  .replace("pages/", "/")
                  .replace("public/", "/")
                  .replace(".tsx", "")
                  .replace("/index.xml", "")
                const route = path === "/index" ? "" : path
                return `
                        <url>
                            <loc>${`${siteMetadata.siteUrl}${route}`}</loc>
                        </url>
                    `
              })
              .join("")}
        </urlset>
    `

  const formatted = prettier.format(sitemap, {
    ...prettierConfig,
    parser: "html",
  })

  // eslint-disable-next-line no-sync
  fs.writeFileSync("public/sitemap.xml", formatted)
  console.info("Success generation of sitemap 🎉")
})()
```

Finally, we need to run this script every time that Next.js builds the application

```js
// next.config.js
module.exports = {
  webpack(config, { dev, isServer }) {
    // Other next.js configuration...

    if (isServer) {
      require("./scriptsPath/generateSitemap")
    }

    return config
  },
}
```

And voila 🎉 our sitemap is generated every time we build our application.

## Final Notes

This example doesn't consider the case in which our paths are generated dynamically, like for example if we have `pages/blog/[slug].tsx`, but I think it will be easy to add that part based on the initial script.

I'm gonna leave in this [Github gist](https://gist.github.com/nsantos16/c8b637b077199eab7cb24c78d7bc7d07) in case you need a boost 😉