Next.js Sitemap

Automatically generate sitemap in Next.js for static routes.

app/sitemap.ts

/**
 * @see https://github.com/iamvishnusankar/next-sitemap/issues/895#issuecomment-2741949689
 */

import fs from "fs";
import path from "path";

import { siteConfig } from "@/config/site";
import type { MetadataRoute } from "next";

async function getStaticRoutes(
  dir = "src/app",
  parentPath = ""
): Promise<string[]> {
  const currentDir = path.join(process.cwd(), dir);
  const entries = fs.readdirSync(currentDir, { withFileTypes: true });

  let routes: string[] = [];

  for (const entry of entries) {
    const fullPath = path.join(currentDir, entry.name);

    if (entry.isDirectory()) {
      // Check if this is a route group (wrapped in parentheses)
      const isRouteGroup =
        entry.name.startsWith("(") && entry.name.endsWith(")");

      // For route groups, don't include the folder name in the path
      const routePath = isRouteGroup
        ? parentPath
        : path.join(parentPath, entry.name);

      const hasPage = ["page.tsx", "page.jsx"].some((file) =>
        fs.existsSync(path.join(fullPath, file))
      );

      if (hasPage && !isRouteGroup) {
        // Only add route if it's not a route group folder
        routes.push(`/${routePath}`);
      }

      const nestedRoutes = await getStaticRoutes(
        path.join(dir, entry.name),
        routePath
      );

      const nestedStaticRoutes = nestedRoutes.filter(
        (route) => !route.match(/\[.+\]/)
      );

      routes = routes.concat(nestedStaticRoutes);
    }
  }

  return parentPath === "" ? ["/", ...routes] : routes;
}

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const allRoutes = (await Promise.all([getStaticRoutes()])).flat();

  return allRoutes.map((route) => ({
    url: encodeURI(`${siteConfig.url}${route}`),
    lastModified: new Date().toISOString(),
    priority: route === "/" ? 1 : 0.8,
  }));
}

Loved this post?

Comments

Interested in
working
with me?