Automatically generate sitemap.xml and robots.txt during build time or runtime for Next.js apps, with support for dynamic routes from both pages and app directories.
- β
Auto-detect routes from
pagesandappdirectories - β
Dynamic route support (e.g.,
/blog/[slug]) - β
Multiple sitemaps with index file (e.g.,
pages.xml,blog.xml,tags.xml) - β Async dynamic paths via config function per sitemap
- β ISR/Revalidation support with API endpoint
- β TypeScript support with full type definitions
- β Lightweight (<10kb, zero dependencies)
- β CLI tool for build integration
- β Customizable priorities, change frequencies, and exclusions per sitemap
npm install next-dynamic-sitemap-generator
# or
yarn add next-dynamic-sitemap-generatorCheck out the examples/ directory for different usage patterns:
examples/simple/- Basic single sitemap setupexamples/complete-backup/- Full-featured multiple sitemaps example
Create sitemap.config.js in your project root:
/** @type {import('next-dynamic-sitemap-generator').SitemapConfig} */
module.exports = {
siteUrl: 'https://example.com',
changefreq: 'daily',
priority: 0.7,
// Optional: dynamically fetch slugs
dynamicPaths: async () => {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return posts.map(p => `/blog/${p.slug}`);
},
};Update your package.json:
{
"scripts": {
"build": "next build && next-dynamic-sitemap-generator"
}
}npm run buildThis generates:
public/sitemap.xmlpublic/robots.txt
For larger websites, you can generate multiple sitemaps with an index file:
/** @type {import('next-dynamic-sitemap-generator').SitemapConfig} */
module.exports = {
siteUrl: 'https://example.com',
// Define multiple sitemaps
sitemaps: [
{
id: 'pages',
changefreq: 'weekly',
priority: 0.8,
// Include only static pages
includeRoutes: ['/', '/about', '/contact'],
exclude: ['/admin/**', '/api/**'],
},
{
id: 'blog',
changefreq: 'daily',
priority: 0.7,
// Include only blog routes
includeRoutes: ['/blog/**'],
// Dynamic blog posts
dynamicPaths: async () => {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return posts.map(post => `/blog/${post.slug}`);
},
},
{
id: 'products',
changefreq: 'weekly',
priority: 0.6,
// Dynamic product pages
dynamicPaths: async () => {
const products = await fetch('https://api.example.com/products').then(r => r.json());
return products.map(product => `/products/${product.id}`);
},
additionalUrls: [
{ loc: '/products', changefreq: 'monthly', priority: 0.9 },
],
},
],
};This generates:
public/sitemap.xml(index file)public/pages.xml(static pages)public/blog.xml(blog posts)public/products.xml(product pages)public/robots.txt
/** @type {import('next-dynamic-sitemap-generator').SitemapConfig} */
module.exports = {
siteUrl: 'https://example.com',
changefreq: 'weekly',
priority: 0.8,
// Fetch dynamic blog posts
dynamicPaths: async () => {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return posts.map(post => `/blog/${post.slug}`);
},
// Exclude certain routes
exclude: ['/admin/**', '/api/**', '/_next/**'],
// Add custom URLs
additionalUrls: [
{
loc: '/special-page',
changefreq: 'monthly',
priority: 0.9,
}
],
// Transform URLs (e.g., add lastmod)
transformUrl: (url) => ({
...url,
lastmod: new Date().toISOString().split('T')[0],
}),
// Custom robots.txt
robotsTxt: `User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/
Sitemap: https://example.com/sitemap.xml`,
};Create /pages/api/rebuild-sitemap.js (or /app/api/rebuild-sitemap/route.js):
import { generateSitemap } from 'next-dynamic-sitemap-generator';
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const result = await generateSitemap();
res.status(200).json({
success: true,
sitemapPath: result.sitemapPath,
urlCount: result.urlCount,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
}Trigger rebuild after CMS updates:
curl -X POST https://yourdomain.com/api/rebuild-sitemapnext-dynamic-sitemap-generator [options]
Options:
-c, --config <path> Path to config file (default: sitemap.config.js)
-o, --output <dir> Output directory (default: public)
--no-robots Skip generating robots.txt
-v, --verbose Enable verbose output
-h, --help Show help
Examples:
next-dynamic-sitemap-generator
next-dynamic-sitemap-generator --config ./config/sitemap.js --output ./dist
next-dynamic-sitemap-generator --no-robots --verbose| Property | Type | Default | Description |
|---|---|---|---|
siteUrl |
string |
Required | Base URL of your site |
changefreq |
ChangeFrequency |
'daily' |
Default change frequency |
priority |
number |
0.7 |
Default priority (0.0-1.0) |
dynamicPaths |
() => Promise<string[]> | string[] |
undefined |
Function to fetch dynamic paths |
exclude |
string[] |
[] |
Patterns to exclude from sitemap |
additionalUrls |
SitemapUrl[] |
[] |
Additional URLs to include |
transformUrl |
(url: SitemapUrl) => SitemapUrl | null |
undefined |
Transform function for URLs |
includeRoot |
boolean |
true |
Include root path / |
robotsTxt |
string |
undefined |
Custom robots.txt content |
sitemaps |
SitemapDefinition[] |
undefined |
Multiple sitemap definitions |
| Property | Type | Default | Description |
|---|---|---|---|
id |
string |
Required | Unique identifier (used in filename) |
changefreq |
ChangeFrequency |
config.changefreq |
Change frequency for this sitemap |
priority |
number |
config.priority |
Priority for this sitemap (0.0-1.0) |
dynamicPaths |
() => Promise<string[]> | string[] |
config.dynamicPaths |
Function to fetch dynamic paths |
exclude |
string[] |
config.exclude |
Patterns to exclude |
additionalUrls |
SitemapUrl[] |
config.additionalUrls |
Additional URLs to include |
transformUrl |
(url: SitemapUrl) => SitemapUrl | null |
config.transformUrl |
Transform function for URLs |
includeRoot |
boolean |
config.includeRoot |
Include root path / |
includeRoutes |
string[] |
undefined |
Route patterns to include (e.g., ['/blog/**']) |
'always''hourly''daily''weekly''monthly''yearly''never'
sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/blog/hello-world</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
</urlset>sitemap.xml (Index)
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://example.com/pages.xml</loc>
<lastmod>2025-01-06T10:30:00.000Z</lastmod>
</sitemap>
<sitemap>
<loc>https://example.com/blog.xml</loc>
<lastmod>2025-01-06T10:30:00.000Z</lastmod>
</sitemap>
<sitemap>
<loc>https://example.com/products.xml</loc>
<lastmod>2025-01-06T10:30:00.000Z</lastmod>
</sitemap>
</sitemapindex>pages.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/about</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</urlset>User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
The package automatically detects routes from:
pages/directory (Next.js 12 and below)app/directory (Next.js 13+)src/pages/directorysrc/app/directory
It supports:
- Static routes:
pages/about.jsβ/about - Dynamic routes:
pages/blog/[slug].jsβ/blog/[slug] - Nested routes:
pages/blog/category/[slug].jsβ/blog/category/[slug] - Index routes:
pages/index.jsβ/
import { generateSitemap, SitemapGenerator } from 'next-dynamic-sitemap-generator';
// Simple generation
const result = await generateSitemap({
configPath: './sitemap.config.js',
outputDir: 'public',
generateRobotsTxt: true,
});
// Advanced usage with custom generator
const generator = new SitemapGenerator('./my-project');
const result = await generator.generate({
configPath: './config/sitemap.js',
outputDir: './dist',
});// sitemap.config.js
module.exports = {
siteUrl: 'https://example.com',
dynamicPaths: async () => {
// Fetch from CMS API
const posts = await fetch('https://cms.example.com/api/posts').then(r => r.json());
const products = await fetch('https://cms.example.com/api/products').then(r => r.json());
return [
...posts.map(p => `/blog/${p.slug}`),
...products.map(p => `/products/${p.slug}`),
];
},
};// /pages/api/webhook.js
import { generateSitemap } from 'next-dynamic-sitemap-generator';
export default async function handler(req, res) {
if (req.method === 'POST') {
// Verify webhook signature (implement your own verification)
// Regenerate sitemap after content update
try {
await generateSitemap();
res.status(200).json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
} else {
res.status(405).json({ error: 'Method not allowed' });
}
}- Node.js: >= 16.0.0
- Next.js: >= 15.0.0
- TypeScript: >= 4.0.0 (for TypeScript projects)
- Minified: ~8.5kb
- Gzipped: ~2.8kb
- Dependencies: 0 (uses only Node.js built-ins)
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT Β© Parsa Jiravand
Inspired by the need for lightweight, dynamic sitemap generation in Next.js applications. Special thanks to the Next.js community for their excellent documentation and tooling.