Skip to content
Consultation

Splitting a Next.js Sitemap into 200-URL Child Files

How I split a Next.js sitemap into 200-URL child files, listed them in robots.txt, and what Google actually reads from a sitemap.

Adityo Guni Waluyo2 min read

The 404 that taught me Next.js emits no sitemap index

I wired generateSitemaps() into the blog's sitemap.ts, deployed, then opened /sitemap.xml to submit it to Search Console. 404. The child files were sitting right there at /sitemap/0.xml, but nothing pointed at them, so Googlebot had no entry point and would never discover the articles.

This isn't my bug. Next.js genuinely does not emit a root /sitemap.xml index when you use generateSitemaps(), so the framework hands you the child URLs and leaves crawlability to you.

The fix is a robots.txt you maintain yourself

So robots.ts lists every child file explicitly: Sitemap: https://adityo.web.id/sitemap/0.xml, then /sitemap/1.xml, and so on. Search Console reads those and crawls each. The split itself is a 200-URL chunk: static routes always land in file 0, published articles follow, walked from the list API across every locale. The ordering has to stay deterministic, because generateSitemaps() computes Math.ceil(total / 200) and sitemap(id) slices the same array. If those ever diverge, a URL silently jumps chunks between runs and you lose crawl continuity. Each article entry carries hreflang alternates built from its translation_group, with x-default pinned to en.

One resilient touch: when the article API is unreachable, the sitemap degrades to static routes instead of throwing a 500. A broken sitemap is worse than a partial one.

Why chunk, and what Google actually reads

Google caps a single sitemap at 50,000 URLs or 50MB. This blog is small today, but articles accrue; chunking now is the protocol-correct shape rather than a later scramble. The more useful lesson is about the XML itself: Google ignores <priority> and <changefreq> entirely. Only <lastmod> feeds recrawl scheduling, and only when it's verifiably accurate. So I set lastmod from the article's updated_at, not the build timestamp.

I'd rather hand-maintain the robots list than bolt on an index route that fights Next's file conventions. Fewer moving parts, and the failure mode is graceful instead of a hard 500.