Why Dynamic Sitemaps?
If you have a static website with 5 pages, a static XML file works fine. But if you are building a Next.js application driven by a CMS (like Sanity or Contentful) or a database, your pages are constantly changing.
You need a dynamic sitemap that generates itself on the fly. Whenever a new blog post is published, the sitemap should instantly reflect the new URL without requiring a manual redeploy.
Using the Next.js App Router (sitemap.js)
With the release of the Next.js App Router, generating a sitemap is incredibly easy. All you have to do is create a file named sitemap.js or sitemap.ts in the root of your app directory.
Next.js will automatically detect this special file and route requests for /sitemap.xml to it. Here is a basic static example:
// app/sitemap.js
export default function sitemap() {
return [
{
url: 'https://mysite.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
{
url: 'https://mysite.com/about',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
}
]
}
Fetching Dynamic Data
The true power of sitemap.js is that it can be an async function. This allows you to connect to your database or CMS, fetch all your dynamic slugs, and map them into the sitemap array.
// app/sitemap.js
import { getAllPosts } from '@/lib/db'
export default async function sitemap() {
const baseUrl = 'https://mysite.com';
// Fetch dynamic posts
const posts = await getAllPosts();
const postUrls = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: post.updatedAt,
changeFrequency: 'weekly',
priority: 0.7,
}));
// Combine static and dynamic
return [
{
url: baseUrl,
lastModified: new Date(),
priority: 1,
},
...postUrls,
]
}
Testing Your Next.js Sitemap
To test it, simply run npm run dev and navigate to localhost:3000/sitemap.xml. Next.js will intercept the request, run your async function, and output a perfectly formatted XML response.
When you deploy to Vercel, this endpoint behaves dynamically, ensuring Google always sees your latest content.
Handling Large Sitemaps with Sitemap Indexes
If your Next.js application generates more than 50,000 URLs, a single sitemap.js file will exceed Google's strict limits. Next.js natively supports generating Sitemap Index files to circumvent this.
By creating a generateSitemaps function alongside your sitemap component, you can paginate your URLs. Next.js will dynamically route requests like /sitemap/0.xml, /sitemap/1.xml based on the pagination chunks.
This built-in pagination support is a massive leap forward for enterprise Next.js eCommerce sites that previously required complex custom API routes to manage millions of product SKUs.
Caching Strategies for Dynamic Sitemaps
While generating sitemaps on the fly is powerful, executing heavy database queries every time a bot requests your sitemap can lead to server overload and timeouts.
To optimize this, you should leverage the Next.js Cache API. By utilizing Next.js Incremental Static Regeneration (ISR) within your sitemap file, you can instruct the framework to cache the XML output for a specific duration (e.g., export const revalidate = 3600 for a 1-hour cache).
This ensures that Googlebot receives a lightning-fast response from the CDN edge cache, rather than triggering a cold boot database query, drastically preserving your server resources while keeping your SEO data relatively real-time.

