
How to add sitemap to Next.js website?
When building modern applications with Next.js, optimizing for search engines is crucial. One of the most fundamental aspects of SEO is a sitemap—a file that helps search engines like Google understand the structure of your website. A sitemap facilitates better crawling, indexing, and ultimately, improved visibility.
In this guide, I’ll walk you through the entire process of creating and adding a sitemap to your Next.js website. I'll also explain the benefits, provide best practices, and show you how to automate sitemap generation and submission to Google Search Console. Whether you're managing a simple blog or a large-scale platform, this guide will help you implement a robust sitemap the right way.
Table of Contents
- What Is a Sitemap and Why It Matters
- Understanding Next.js and Static/Dynamic Pages
- Planning Your Sitemap Strategy
- Installing Necessary Packages
- Creating a Dynamic Sitemap Script
- Setting Up
getServerSideProps
for Real-Time Generation - Automating Sitemap Generation in Static Builds (
getStaticProps
) - Adding a Robots.txt file
- Submitting Your Sitemap to Google
- Testing and Debugging Your Sitemap
- Sitemap Best Practices
- Troubleshooting Common Issues
- Final Thoughts
What Is a Sitemap and Why It Matters?
A sitemap is an XML file that lists the URLs on your website. It also includes metadata such as the last modification date, change frequency, and priority. Here’s what makes it important:
- Helps search engines discover URLs effectively.
- Improves SEO for large or complex websites.
- Assists bots in understanding page relationships.
- Ensures timely indexing of new or updated content.
In short, it enhances your site’s discoverability, which can positively affect your rankings.
Understanding Next.js: Static vs. Dynamic Pages
Before creating your sitemap, you need to understand your site’s nature. Next.js supports:
- Static Generation (SSG) — builds at compile time via
getStaticProps
. - Server-Side Rendering (SSR) — fetches data on each request via
getServerSideProps
. - Dynamic Routes — routes that are generated based on data (e.g.,
/blog/[slug].tsx
).
Knowing how your pages are built will determine how you properly generate your sitemap.
Planning Your Sitemap Strategy
Here's how I usually plan:
- List all permanent routes: Home, About, Contact, etc.
- Identify dynamic pages: Blog posts, products, categories.
- Decide how often they change: This influences the
changefreq
tag. - Exclude admin or private routes: These shouldn't be in sitemaps.
Organizing things upfront helps avoid poor configurations later.
Installing Necessary Packages
First, I install the sitemap generation tools. I recommend the next-sitemap
package for its seamless integration and support for static and dynamic routes.
npm install next-sitemap
Or with Yarn:
yarn add next-sitemap
Creating a Dynamic Sitemap Script with next-sitemap
After installation, I create a configuration file in the root of the project:
next-sitemap.config.js
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: "https://www.example.com", // Replace with your domain
generateRobotsTxt: true,
changefreq: "daily",
priority: 0.7,
sitemapSize: 1000,
exclude: ["/admin/*"],
};
Updates to package.json
Next, I add a build script to automatically generate the sitemap during the production build.
"scripts": {
"postbuild": "next-sitemap"
}
Now, when I run next build
, the sitemap files generate automatically.
Setting Up getServerSideProps
for Real-Time Generation (Advanced Use)
If my content changes frequently or relies on external APIs, I sometimes opt for a real-time dynamic sitemap using getServerSideProps
.
Create Route: pages/sitemap.xml.js
export async function getServerSideProps({ res }) {
const baseUrl = "https://www.example.com";
const posts = await fetch(`${baseUrl}/api/posts`).then((res) => res.json());
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${posts
.map((post) => {
return `
<url>
<loc>${baseUrl}/blog/${post.slug}</loc>
<lastmod>${new Date(post.updatedAt).toISOString()}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
`;
})
.join("")}
</urlset>`;
res.setHeader("Content-Type", "text/xml");
res.write(sitemap);
res.end();
return {
props: {},
};
}
export default function Sitemap() {
return null;
}
With this approach, your sitemap is always up to date. Note that it incurs server costs due to SSR on every request.
Automating Sitemap in Static Builds with getStaticProps
If I prefer the performance benefits of pre-rendering and don’t need real-time updates, static generation is better.
I create the same file as above but within getStaticProps
. Then I fetch the blog data from a local source.
This is a simplified example, but the concept is the same.
Adding a Robots.txt File
A robots.txt file tells crawlers where to look (or not look). next-sitemap
can generate this for me.
Sample robots.txt
User-agent: *
Allow: /
Sitemap: https://www.example.com/sitemap.xml
With the generateRobotsTxt
flag on in next-sitemap.config.js
, the package does this automatically.
Submitting Your Sitemap to Google
Once the sitemap is ready, I update Google Search Console:
- Go to Google Search Console.
- Select the correct property.
- Open “Sitemaps” on the left menu.
- Enter
sitemap.xml
in the field. - Click Submit.
Google will process it and crawl your site more efficiently.
Testing and Debugging Your Sitemap
Before publishing, I test it thoroughly:
- Open
https://yourdomain.com/sitemap.xml
— ensure it loads. - Use tools like XML Sitemap Validator.
- Manually inspect URLs to ensure accuracy.
For issues with missing URLs or broken links, I double-check dynamic route generation.
Sitemap Best Practices
I've compiled expert-level tips here:
- Keep URLs canonical: Ensure each URL is the preferred version (https over http, trailing slashes consistent).
- Include only indexable pages: No need to list 404s, redirects, or admin panels.
- Update
lastmod
accurately: Helps search engines detect content freshness. - Split sitemaps if large: If over 50,000 URLs or 50MB, break into sitemap indexes.
- Monitor crawl stats: Use Google Search Console’s Coverage and Sitemap features.
Troubleshooting Common Issues
Duplicate URLs
Ensure your dynamic routes are not listed multiple times. Use URL deduplication with .filter()
in JS or avoid conflicting slug generation.
Missing Pages
Revisit getStaticPaths
or data sources. The issue might be upstream (e.g., headless CMS delays causing missing data during build).
Sitemap Not Updating
Make sure your build system triggers postbuild
. If using CI/CD (like Vercel), confirm deployment hooks are properly set.
Frequently Asked Questions About How to add sitemap to Next.js website?
Yes, even small websites benefit from sitemaps. While search engines can crawl your site without one, a sitemap ensures all pages are discovered quickly, especially if your site has new or infrequently updated content.
- Static sitemap: Manually created or generated at build time (best for sites with fixed pages).
- Dynamic sitemap: Generated on-demand (best for blogs, e-commerce, or CMS-driven sites where content changes frequently).
Yes! next-sitemap works well with ISR. The sitemap regenerates during builds or revalidation, keeping URLs up to date.
With next-sitemap, use the exclude option in the config file:
exclude: ["/admin", "/private/*"];
Yes. While Google is the most common, submitting to Bing Webmaster Tools and Yandex Webmaster ensures broader indexing coverage.
Final Thoughts
Implementing a sitemap for your Next.js website is both essential and straightforward when approached correctly. Whether your site is static, dynamic, or hybrid, you have flexible tools to build robust, crawlable structures.
I've used both automated and manual methods and found that next-sitemap
covers 95% of use cases. For more granular control, dynamic script approaches offer flexibility. Combine this with a solid robots.txt and active monitoring, and your SEO foundation is strong.
If there’s one piece of advice I’ll leave you with: Don’t set and forget. I revisit my sitemaps with every major update or when content structure changes.