Back to posts

How I Automated My Blog Sitemap in Next.js (and You Can Too)

Hamid Sabri / October 27, 2025

building a dynamic sitemap in Next.js

If you’ve ever shipped a new blog post, pushed to production, and then realized your sitemap didn’t update - you’ll understand my frustration.

I recently went through that while building my own blog. Each post lives in the /content folder as an .mdx file, and every time I published something new, I had to manually add it to the sitemap. It wasn’t hard, but it was annoying and prone to forgetting.

So, I automated it.

This post walks you through exactly how I did it: a simple Next.js sitemap that updates automatically whenever you add a new MDX file. No external dependencies, no plugins, just clean JavaScript.


Why I Wanted to Automate the Sitemap

Google relies on your sitemap to understand what’s new and what’s updated on your website. I wanted:

  • ✅ My new posts and projects to be automatically indexed
  • ✅ No manual edits every time I hit publish
  • ✅ SEO hygiene that just takes care of itself

And since my site already builds dynamically with MDX, I realized: why not let Next.js do the same for the sitemap?


The Setup

Here’s my site structure:

/content
 ├─ posts/
    ├─ n8n-monitoring-grafana.mdx
    ├─ low-stock-alerts.mdx
    └─ responsive-navbar.mdx
 ├─ projects/
    ├─ roots-the-brand.mdx
    ├─ bellroy-section.mdx
    └─ whatsapp_order_confirmation.mdx
/app
 └─ sitemap.js

Every MDX file automatically becomes a live page - either a post or a project. The goal: have all of them instantly show up in sitemap.xml without touching the file manually.


Step 1 - Writing the Dynamic Sitemap Function

Here’s the core of the automation - the entire /app/sitemap.js file:

import fs from "fs";
import path from "path";

export default function sitemap() {
  const baseUrl = "https://sabrihamid.com";
  const now = new Date();

  // Helper to scan /content subfolders
  const generateUrls = (subdir, priority) => {
    const dirPath = path.join(process.cwd(), "content", subdir);
    if (!fs.existsSync(dirPath)) return [];

    return fs
      .readdirSync(dirPath)
      .filter((file) => file.endsWith(".mdx") && !file.startsWith("_draft"))
      .map((file) => {
        const slug = file.replace(".mdx", "");
        return {
          url: `${baseUrl}/${subdir}/${slug}`,
          lastModified: now,
          changeFrequency: "weekly",
          priority,
        };
      });
  };

  const posts = generateUrls("posts", 0.9);
  const projects = generateUrls("projects", 0.8);

  const staticPages = [
    {
      url: `${baseUrl}`,
      lastModified: now,
      changeFrequency: "monthly",
      priority: 1,
    },
    {
      url: `${baseUrl}/contact`,
      lastModified: now,
      changeFrequency: "monthly",
      priority: 0.5,
    },
    {
      url: `${baseUrl}/posts`,
      lastModified: now,
      changeFrequency: "weekly",
      priority: 1,
    },
    {
      url: `${baseUrl}/projects`,
      lastModified: now,
      changeFrequency: "weekly",
      priority: 1,
    },
  ];

  return [...staticPages, ...posts, ...projects];
}

I love how simple this is. No need for next-sitemap or any third-party package - just Node’s fs and path modules.


Step 2 - How It Works

  • Reads all .mdx files from /content/posts and /content/projects
  • Builds URLs automatically based on filenames
  • Skips drafts (files starting with _draft)
  • Outputs a full sitemap consumed by Next.js at /sitemap.xml

When you deploy or rebuild your site, the sitemap refreshes automatically. Every new post or project you push gets indexed.


Step 3 - Testing It

Run your app and open:

http://localhost:3000/sitemap.xml

You’ll instantly see an up-to-date XML sitemap - no configuration needed.


Optional: Show Real lastModified Dates

You can go one step further by replacing now with each file’s actual last modified date:

const stats = fs.statSync(path.join(dirPath, file));
const lastModified = stats.mtime;

That way, Google knows when each page was last updated - great for SEO freshness.


Step 4 - Submit It to Google

Once deployed:

  1. Go to Google Search Console → Index → Sitemaps
  2. Enter your sitemap URL:
    https://sabrihamid.com/sitemap.xml
    
  3. Click Submit

Google will now automatically pick up every new post or project you create. sitemap screenshot

Final Thoughts

This one small automation makes my publishing workflow feel complete. Now, when I hit “publish,” I know my content is live, discoverable, and indexed - automatically.

It’s one of those behind-the-scenes wins that make your site feel like a real product.

✅ Works with any MDX-based blog
✅ No extra dependencies
✅ Keeps SEO fresh automatically
✅ Excludes drafts cleanly

I use this method on my own site - and I can’t imagine going back.