DEV Community

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

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

Nicolas Santos
I'm a software engineer based in Montevideo, UY specializing in building exceptional websites, applications, and everything in between
Originally published at santosnicolas.com ・2 min read

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 😉

Discussion (0)