Skip to content

Automatically generate sitemap.xml and robots.txt for Next.js apps with support for dynamic routes and ISR

License

Notifications You must be signed in to change notification settings

parsajiravand/next-dynamic-sitemap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

6 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Next Dynamic Sitemap Generator

npm version License: MIT

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 pages and app directories
  • βœ… 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 setup
  • examples/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 build

This generates:

  • public/sitemap.xml
  • public/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/ directory
  • src/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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. 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.

About

Automatically generate sitemap.xml and robots.txt for Next.js apps with support for dynamic routes and ISR

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published