DEV Community

Cover image for Sitemap: What is and how to generate it for a Next.js App
Nicolas Santos
Nicolas Santos

Posted on • Originally published at santosnicolas.com

Sitemap: What is and how to generate it for a Next.js App

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

<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>
Enter fullscreen mode Exit fullscreen mode

How I generate the sitemap on Next.js

We basically need to add

<url>
    <loc>${routePage}</loc>
</url>
Enter fullscreen mode Exit fullscreen mode

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, this lib allows us to get the name of the files based on regex URL on our folder structure.

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']
})()
Enter fullscreen mode Exit fullscreen mode

With fs and prettier we can format and write our generated file(sitemap.xml) to located in the public folder.

// 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 ๐ŸŽ‰")
})()
Enter fullscreen mode Exit fullscreen mode

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

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

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

    return config
  },
}
Enter fullscreen mode Exit fullscreen mode

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 in case you need a boost ๐Ÿ˜‰

Top comments (0)