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,
}));
}