next-dynamic-sitemap-generator
v1.0.1
Published
Automatically generate sitemap.xml and robots.txt for Next.js apps with support for dynamic routes and ISR
Maintainers
Readme
Next Dynamic Sitemap Generator
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.
✨ Features
- ✅ 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
🚀 Installation
npm install next-dynamic-sitemap-generator
# or
yarn add next-dynamic-sitemap-generator📁 Examples
Check out the examples/ directory for different usage patterns:
examples/simple/- Basic single sitemap setupexamples/complete-backup/- Full-featured multiple sitemaps example
📖 Basic Usage
1. Create Configuration
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}`);
},
};2. Add to Build Script
Update your package.json:
{
"scripts": {
"build": "next build && next-dynamic-sitemap-generator"
}
}3. Build Your App
npm run buildThis generates:
public/sitemap.xmlpublic/robots.txt
📄 Multiple Sitemaps
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
🔧 Advanced Configuration
Dynamic Paths with API
/** @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`,
};API Route for Rebuilding
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-sitemap🛠️ CLI Options
next-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📁 Configuration Reference
SitemapConfig
| 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 |
SitemapDefinition
| 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/**']) |
ChangeFrequency
'always''hourly''daily''weekly''monthly''yearly''never'
📄 Generated Files
Single Sitemap
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>Multiple Sitemaps
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>robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml🔍 Route Detection
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→/
🏗️ Programmatic Usage
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',
});🤝 Integration Examples
With Content Management Systems
// 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}`),
];
},
};With Webhooks
// /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' });
}
}🚨 Requirements
- Node.js: >= 16.0.0
- Next.js: >= 15.0.0
- TypeScript: >= 4.0.0 (for TypeScript projects)
📦 Bundle Size
- Minified: ~8.5kb
- Gzipped: ~2.8kb
- Dependencies: 0 (uses only Node.js built-ins)
🤝 Contributing
- 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
📄 License
MIT © Parsa Jiravand
🙏 Acknowledgments
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.
