Sitemaps in Next.js
A sitemap is an XML file that lists all the pages on your site, helping search engines discover your content. Next.js makes sitemap generation straightforward.
Why Sitemaps Matter
Sitemaps help search engines:
- Discover pages - Especially new or deep pages
- Prioritize crawling - Based on priority and change frequency
- Understand structure - See your site's organization
- Stay updated - Know when content changes
Automatic Sitemap Generation
Next.js can generate sitemaps automatically using the sitemap.ts file:
// app/sitemap.ts
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
{
url: 'https://example.com/about',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: 'https://example.com/courses',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
]
}
This generates /sitemap.xml automatically.
Sitemap Properties
| Property | Description | Values |
|---|---|---|
url | Full URL of the page | Required, absolute URL |
lastModified | Last modification date | Date or ISO string |
changeFrequency | How often content changes | always, hourly, daily, weekly, monthly, yearly, never |
priority | Relative importance | 0.0 to 1.0 (default 0.5) |
Dynamic Sitemap with Database Content
For sites with dynamic content, fetch your data and generate entries:
// app/sitemap.ts
import type { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/posts'
import { getAllCourses } from '@/lib/courses'
export default async function sitemap(): MetadataRoute.Sitemap {
const baseUrl = 'https://example.com'
// Static pages
const staticPages: MetadataRoute.Sitemap = [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
{
url: `${baseUrl}/courses`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
},
{
url: `${baseUrl}/blog`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.9,
},
]
// Dynamic blog posts
const posts = await getAllPosts()
const postPages: MetadataRoute.Sitemap = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: 'weekly',
priority: 0.7,
}))
// Dynamic courses
const courses = await getAllCourses()
const coursePages: MetadataRoute.Sitemap = courses.map((course) => ({
url: `${baseUrl}/courses/${course.slug}`,
lastModified: new Date(course.updatedAt),
changeFrequency: 'weekly',
priority: 0.8,
}))
return [...staticPages, ...postPages, ...coursePages]
}
Multiple Sitemaps (Sitemap Index)
For large sites (over 50,000 URLs or 50MB), split into multiple sitemaps:
// app/sitemap.ts - Returns sitemap index
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com/sitemaps/pages.xml',
lastModified: new Date(),
},
{
url: 'https://example.com/sitemaps/blog.xml',
lastModified: new Date(),
},
{
url: 'https://example.com/sitemaps/courses.xml',
lastModified: new Date(),
},
]
}
Then create individual sitemap files:
// app/sitemaps/blog.xml/route.ts
export async function GET() {
const posts = await getAllPosts()
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${posts.map(post => `
<url>
<loc>https://example.com/blog/${post.slug}</loc>
<lastmod>${post.updatedAt}</lastmod>
</url>
`).join('')}
</urlset>`
return new Response(xml, {
headers: { 'Content-Type': 'application/xml' },
})
}
Sitemap Best Practices
Do
- Update lastModified when content actually changes
- Keep under 50,000 URLs per sitemap file
- Include only canonical URLs (not duplicates)
- Submit to Google Search Console
- Use absolute URLs
Don't
- Include noindex pages
- Include redirected URLs
- Include duplicate content
- Update lastModified without content changes
- Include error pages (404, 500)
Submitting Your Sitemap
Google Search Console
- Go to Search Console
- Select your property
- Navigate to Sitemaps
- Enter
sitemap.xml - Click Submit
robots.txt Reference
Add sitemap location to robots.txt:
// app/robots.ts
export default function robots(): MetadataRoute.Robots {
return {
rules: { userAgent: '*', allow: '/' },
sitemap: 'https://example.com/sitemap.xml',
}
}
Summary
In this lesson, you learned:
- Why sitemaps are important for SEO
- Creating static and dynamic sitemaps in Next.js
- Sitemap properties and their meanings
- Handling large sites with sitemap indexes
- Best practices for sitemap maintenance
- Submitting sitemaps to search engines
In the next lesson, we'll cover robots.txt configuration.

