
The Modern Way to Generate Sitemaps in Next.js (2025 Guide)
Published: 6/16/2025
Sitemaps are crucial for SEO, helping search engines discover and index your pages efficiently. With Next.js 13+, generating a sitemap has become easier than ever no more manual XML files or build scripts!
In this guide, I'll show you how to:
✅ Automatically generate a sitemap (dynamic & always up-to-date)
✅ Integrate with Sanity CMS (or any headless CMS)
✅ Optimize for Vercel (edge caching, zero maintenance)
Why the Old Approach is Outdated
Traditionally, developers:
- Wrote a script to generate
sitemap.xml
- Ran it during builds (
npm run build
) - Committed the file to Git
Problems?
- Stale content between rebuilds
- Manual updates for new pages
- Build complexity with large sites
The Modern Solution: sitemap.js
Next.js 13+ introduced file-based sitemap generation. Instead of a static file, your sitemap is:
🔹 Generated on-demand when crawled
🔹 Always fresh (pulls latest CMS content)
🔹 Zero config (works out of the box)
Step-by-Step Implementation
1. Create app/sitemap.js
import { client } from "@/sanity/client";
import { getBaseUrl } from "@/lib/getBaseUrl";export default async function sitemap() {
const baseUrl = getBaseUrl();
// 1. Static routes
const staticRoutes = [
{ url: "/", lastModified: new Date() },
{ url: "/blog", lastModified: new Date() },
];
// 2. Dynamic routes (from Sanity)
const posts = await client.fetch(` *[_type == "post"] { slug, _updatedAt } `);
const dynamicRoutes = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug.current}`,
lastModified: new Date(post._updatedAt),
}));
// 3. Combine all routes
return [...staticRoutes, ...dynamicRoutes];
}
The static routes points to the part of your websites that are relatively static - No frequent updates.
The dynamic routes points to the part of you websites that requires constant updates - individual blogs.
In my code, I have fetched all my post from sanity and I used the slug( similar to id) to create distinct urls for each blog.
2. Ensure getBaseUrl()
Works in Production
// lib/getBaseUrl.tsexport function getBaseUrl() {
// Vercel auto-sets this
if (process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}`;
}
// Local dev fallback
return "http://localhost:3000";
}
3. Verify It Works
Visit:
https://yourdomain.com/sitemap.xml
If you see something like this , congratulations! you have successfully created your site map for your Next.js application
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://yourdomain.com</loc>
<lastmod>2024-02-20T00:00:00.000Z</lastmod>
</url>
<url>
<loc>https://yourdomain.com/blog</loc>
<lastmod>2024-02-20T00:00:00.000Z</lastmod>
</url>
<url>
<loc>https://yourdomain.com/blog/my-first-post</loc>
<lastmod>2024-02-15T00:00:00.000Z</lastmod>
</url>
</urlset>
Go ahead to your google search console(https://search.google.com/search-console/about) , submit your sitemap for verfication and hope that google indexes your page asap.
Why This is Better
🚀 Always Updated – No rebuilds needed when content changes
⚡ Fast – Vercel edge-caches responses
🔍 SEO-Friendly – GoogleBot gets fresh URLs instantly
Bonus: Add a robots.txt
File
Create app/robots.js
:
import { getBaseUrl } from "@/lib/getBaseUrl";export default function robots() {
return {
rules: [
{ userAgent: "*", allow: "/" },
{ userAgent: "*", disallow: "/admin" },
],
sitemap: `${getBaseUrl()}/sitemap.xml`,
};
}
This auto-generates robots.txt
pointing to your sitemap!
The generate robot.txt will look like this
User-agent: *
Allow: / # Lets bots crawl the entire site
Disallow: /admin # Blocks crawling of /admin
robots.txt gives google bot the permission to crawl certain parts of your website and denies them access to others.
Final Thoughts
Gone are the days of manual sitemap management. With Next.js dynamic sitemaps, you get:
✔ Automatic updates (CMS changes reflect instantly)
✔ No build scripts (zero maintenance)
✔ Perfect SEO (Google loves fresh sitemaps)
Try it today! Your SEO rankings (and sanity) will thank you. 🚀
Want to see it in action? Check out my live sitemap at:
👉 https://richardwebsites.vercel.app/sitemap.xml
Found this helpful? Share it with other devs! 🚀