How to build a sitemap?

Setup

Install the following packages:

npm i globby
npm i -D prettier

Add sitemap script

Info

The following example assumes files list for dynamic Next.js routes is imported from the database created with MarkdownDB package. You need to adjust it to your specific use case.

Add the following script to e.g. /scripts/sitemap.mjs and adjust it to work with your app:

// /scripts/sitemap.mjs
import { writeFileSync } from "fs";
import { globby } from "globby";
import prettier from "prettier";

// Change this to import data from your content layer (if needed)
import clientPromise from "../lib/mddb.mjs";

export default async function sitemap() {
  const prettierConfig = await prettier.resolveConfig("path-to-your-prettier-config");

  // Change this to create a list of page paths
  const mddb = await clientPromise;
  const allFiles = await mddb.getFiles({ extensions: ["mdx", "md"] });
  const contentPages = allFiles
    .filter((x) => !x.metadata?.isDraft)
    .map((x) => `/${x.url_path}`);
  const pages = await globby([
    "pages/*.(js|tsx)",
    "!pages/_*.(js|tsx)",
    "!pages/api",
    "!pages/404.(js|tsx)",
    "!pages/**/\\[\\[*\\]\\].(js|tsx)", // pages/[[...slug]].tsx
  ]);
  const allPages = pages.concat(contentPages);
  // end

  // Replace this with your site domain
  const siteDomain = "your-site-domain";

  const sitemap = `
    <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      ${allPages
        .map((page) => {
          const path = page
            .replace("pages/", "/")
            .replace("public/", "/")
            .replace(/\.js|.tsx|.mdx|.md[^\/.]+$/, "")
            .replace("/feed.xml", "");
          const route = path === "/index" ? "" : path;
          return `
            <url>
              <loc>${siteDomain}${route}</loc>
            </url>
          `;
        })
        .join("")}
    </urlset>
  `;

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

  if (siteUrl) {
    writeFileSync("public/sitemap.xml", formatted);
    console.log("Sitemap generated...");
  }
}

await sitemap();
process.exit();

Execute script before each build

Add this to the scripts section in your package.json to regenerate the sitemap before each build.


"prebuild": "node ./scripts/sitemap.mjs",