In last two blogs, I explained how to create a sitemap for a website in your NextJS project using the next-sitemap package and manually. In this blog, I will explain how you can create a fully customized sitemap for your website in NextJS using a custom postbuild script.
But if you are rocking a static website, you can use the next-sitemap package to create a sitemap as it will be much easier and faster. But if you are looking to create a fully customized sitemap, you can use the custom postbuild script approach.
Why this technique is important? Because sometimes you need to create a sitemap with a specific requirement like
- Each year/folder/collection etc will have its own sitemap
- Exclude some pages from the sitemap
- Add custom priority, changefreq, and lastmod for each page
- Add custom tags for each page and many more.
But unlike our first article in series, we will do it manually. This is will fully automated and you can customize it as per your requirements. So brew a coffee and lets get started ๐
Complementary Video Tutorial
If you prefer a visual walkthrough, check out this helpful video that complements the steps in this blog:
"Click the above player or here to watch the video on YouTube."
Prerequisites
- Basic knowledge of NextJS
- Basic knowledge of JavaScript
- Basic knowledge of XML
Table of Content
- Step 1: Create a custom postbuild script
- Step 2: Install globby package
- Step 3: Add the following code in the
generate-sitemap.mjs
file - Step 4: Add the following script in your
package.json
- Step 5: Run the build command
- Step 6: Check the sitemap.xml file in the public folder of your project
- Conclusion
- Get in touch
Step 1: Create a custom postbuild script
Create a scripts
folder in the root of your project and add a generate-sitemap.mjs
file in it.
Step 2: Install globby package
npm install globby
Wait a minute, what is globby? and why we need it?
Globby is a package that helps you to match files using the patterns the shell uses, like stars and stuff. We will use it to get all the pages from project build folder.
Step 3: Add the following code in the generate-sitemap.mjs
file
Before I share whole code, I would like to explain few key points in the code.
- siteUrl: Base URL of your website
-
fullUrl: Function that will return the full URL of the page ie
siteUrl + path
- defaultConfig: Default configuration for each page in the sitemap
- homeConfig: Configuration for the home page
-
nextApproach: Approach you are using for the your NextJS project, it can be
app
orpages
,
nextApproach is important because the build path for NextJS pages router is
.next/server/pages
and for NextJS app router is.next/server/app
. So depending on the approach you are using, you need to change the path in the code.
import { writeFileSync } from 'fs'
import { globby } from 'globby'
const siteUrl = 'https://example.com'
const fullUrl = (path) => siteUrl + path
const defaultConfig = {
changefreq: 'weekly',
priority: '0.7',
lastmod: new Date().toISOString(),
}
const homeConfig = {
loc: '/',
changefreq: 'monthly',
priority: '1.0',
lastmod: defaultConfig.lastmod,
}
const nextApproach = 'app' // app or pages
const serverPath = `.next/server/${nextApproach}`
async function generateSitemap() {
// Grub Pages from build
const buildPages = await globby([
// *** Include ***
// include all html files and nested html files
`${serverPath}/*.html`,
`${serverPath}/**/*.html`,
// *** Exclude ***
// /index.html is same as / so we will exclude it, along with 404, 500 and _not-found pages, cool right?
`!${serverPath}/index.html`,
`!${serverPath}/404.html`,
`!${serverPath}/_not-found.html`,
`!${serverPath}/500.html`,
])
const sitemapStr = `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${fullUrl(homeConfig.loc)}</loc>
<lastmod>${homeConfig.lastmod}</lastmod>
<changefreq>${homeConfig.changefreq}</changefreq>
<priority>${homeConfig.priority}</priority>
</url>
${buildPages
.map((page) => {
const path = page.replace(serverPath, '').replace('.html', '')
const loc = fullUrl(path)
const lastmod = new Date().toISOString()
let changefreq = defaultConfig.changefreq
let priority = defaultConfig.priority
if (path === '/products') {
priority = '0.9' // Higher priority for the index products page
changefreq = 'daily'
} else if (path.includes('/products/')) {
priority = '0.6' // Higher priority for the slug products page
changefreq = 'daily'
}
return `<url>
<loc>${loc}</loc>
<lastmod>${lastmod}</lastmod>
<changefreq>${changefreq}</changefreq>
<priority>${priority}</priority>
</url>
`
})
.join('')}
</urlset>
`
writeFileSync(`public/sitemap.xml`, sitemapStr)
}
generateSitemap()
This code will produce a single sitemap.xml file in the public folder of your project.
But if you want to create multiple sitemaps, you create a multiple globby configurations and write multiple sitemap files in the public folder.
Continue reading to learn how to create multiple sitemaps or skip to next step if you only want to create a single sitemap.
Very Important Point below if you going to have multiple sitemaps in your project.
- First Point: Include all the sitemaps in the
robots.txt
file, so that search engines can find all the sitemaps. - Second Point: Create a Index sitemap file that will include all the sitemaps links.
Side Quest: How to create multiple sitemaps
To create multiple sitemaps, you can create multiple globby configurations and write multiple sitemap files in the public folder.
import { writeFileSync } from 'fs'
import { globby } from 'globby'
const siteUrl = 'https://example.com'
const fullUrl = (path) => siteUrl + path
const defaultConfig = {
changefreq: 'weekly',
priority: '0.7',
lastmod: new Date().toISOString(),
}
const homeConfig = {
loc: '/',
changefreq: 'monthly',
priority: '1.0',
lastmod: defaultConfig.lastmod,
}
const nextApproach = 'app' // app or pages
const serverPath = `.next/server/${nextApproach}`
async function generateSitemap() {
// Grub Pages from build for products
const productPages = await globby([
// *** Include ***
// include all html products files and nested html files
`${serverPath}/products/*.html`,
`${serverPath}/products/**/*.html`,
// *** Exclude ***
// /index.html is same as / so we will exclude it
`!${serverPath}/products/index.html`,
])
// Create multiple sitemaps
const productSitemapStr = `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${fullUrl('/products')}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>0.9</priority>
</url>
${productPages
.map((page) => {
const path = page.replace(serverPath, '').replace('.html', '')
const loc = fullUrl(path)
const lastmod = new Date().toISOString()
const changefreq = 'daily'
const priority = '0.8'
return `<url>
<loc>${loc}</loc>
<lastmod>${lastmod}</lastmod>
<changefreq>${changefreq}</changefreq>
<priority>${priority}</priority>
</url>
`
})
.join('')}
</urlset>
`
writeFileSync(`public/product-sitemap.xml`, productSitemapStr)
// You can do the same for other sitemaps. But lets move on to the next step.
// lets create a sitemap for rest of the pages
const restPages = await globby([
// *** Include ***
// include all html files and nested html files
`${serverPath}/*.html`,
`${serverPath}/**/*.html`,
// *** Exclude ***
// /index.html is same as / so we will exclude it, along with 404, 500 and _not-found pages, cool right?
`!${serverPath}/index.html`,
`!${serverPath}/404.html`,
`!${serverPath}/_not-found.html`,
`!${serverPath}/500.html`,
// Exclude products pages
`!${serverPath}/products/*.html`,
`!${serverPath}/products/**/*.html`,
])
const restSitemapStr = `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${fullUrl(homeConfig.loc)}</loc>
<lastmod>${homeConfig.lastmod}</lastmod>
<changefreq>${homeConfig.changefreq}</changefreq>
<priority>${homeConfig.priority}</priority>
</url>
${restPages
.map((page) => {
const path = page.replace(serverPath, '').replace('.html', '')
const loc = fullUrl(path)
const lastmod = new Date().toISOString()
let changefreq = defaultConfig.changefreq
let priority = defaultConfig.priority
return `<url>
<loc>${loc}</loc>
<lastmod>${lastmod}</lastmod>
<changefreq>${changefreq}</changefreq>
<priority>${priority}</priority>
</url>
`
})
.join('')}
</urlset>
`
writeFileSync(`public/rest-sitemap.xml`, restSitemapStr)
// Create a index sitemap file
const indexSitemapStr = `<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>${fullUrl('/product-sitemap.xml')}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
</sitemap>
<sitemap>
<loc>${fullUrl('/rest-sitemap.xml')}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
</sitemap>
</sitemapindex>
`
writeFileSync(`public/sitemap.xml`, indexSitemapStr)
}
generateSitemap()
This code will produce multiple sitemap files in the public folder of your project. You can create as many sitemaps as you want by creating multiple globby configurations and writing multiple sitemap files in the public folder.
Note: Include all the sitemaps in the
robots.txt
file, so that search engines can find all the sitemaps.
Step 4: Add the following script in your package.json
"scripts": {
"build": "next build",
"postbuild": "node ./scripts/generate-sitemap.mjs"
}
Step 5: Run the build command
npm run build
Step 6: Check the sitemap.xml file in the public folder of your project
| app
| /pages
| /public
| /sitemap.xml
| /product-sitemap.xml // if you created multiple sitemaps
| /rest-sitemap.xml // if you created multiple sitemaps
on successful build, you will see a sitemap.xml
file in the public folder of your project. If you created multiple sitemaps, you will see multiple sitemap files in the public folder.
Conclusion
With this approach, you can create a fully customized sitemap for your website in NextJS. You can create multiple sitemaps with specific requirements like each year/folder/collection etc will have its own sitemap, exclude some pages from the sitemap, add custom priority, changefreq, and lastmod for each page, add custom tags for each page, and many more.
But what if you have a dynamic website and data changes frequently? In the next blog, I will explain how you can extend this approach to create a include dynamic data in the sitemap.
Till then, Happy Coding ๐
Get in touch
Platform | Handle |
---|---|
Youtube | @thesohailjafri |
X/Twitter | @thesohailjafri |
@thesohailjafri | |
@thesohailjafri | |
Github | @thesohailjafri |
Top comments (0)