Skip to content
AS

Your Sitemap Will Drift, So Derive It

  • seo
  • nextjs
  • architecture

A sitemap that lists pages you no longer have, and omits pages you just shipped, is worse than no sitemap. It teaches search engines to distrust the file. The usual cause is not carelessness — it is that the sitemap is a second copy of information that already exists in your routing.

Two copies always drift

Adding a page in the Next.js App Router means creating a folder. Nothing about that action touches your sitemap. So the sitemap is correct only for as long as everyone remembers a step that no tool enforces.

Give it a few months and a few contributors, and it will be wrong.

Declare routes once

Instead, write the routes down in one place and let everything else read from it:

// src/lib/seo/routes.ts
export const staticRoutes = [
  { path: "/", label: "Home", priority: 1.0, showInNav: false },
  { path: "/projects", label: "Projects", priority: 0.9, showInNav: true },
  { path: "/blog", label: "Writing", priority: 0.9, showInNav: true },
  { path: "/about", label: "About", priority: 0.8, showInNav: true },
] as const;

The navigation maps over it. The footer maps over it. sitemap.ts maps over it and appends the dynamic slugs. There is now exactly one copy of the answer to "what pages does this site have?"

Then close the loop with a test

Deriving the sitemap removes most of the drift, but not all of it: someone can still create app/(site)/speaking/page.tsx and never add it to the registry. A test catches that:

test("every route folder is registered", () => {
  const folders = readRouteFoldersFromDisk();
  const registered = staticRoutes.map((r) => r.path);
  expect(folders.sort()).toEqual(registered.sort());
});

Now the filesystem and the registry cannot disagree without the build going red.

The general shape

This pattern is not really about sitemaps. It is: when the same fact is written in two places, a tool should either derive one from the other or assert they match. Anything you rely on a human to remember is a regression waiting for a busy week.