Generating a sitemap for a Next.js application
- typescript
- next.js
- node.js
- sitemap
- seo
Introduction
A sitemap is a file that contains a list of URLs on a website.
It helps search engines to discover and index the content of a website.
It's supposed to be in XML
format and it should be placed in the root directory of the website.
In this very short blog post, we will learn how to generate a sitemap for a Next.js application using a custom type-safe script.
Prerequisites
- Obviously you need a Next.js application.
- Basic knowledge of TypeScript.
- Basic knowledge of Node.js.
Looking into the build output of this website
If you don't already know, this website is built using Next.js and more specifically with app router. When you build a Next.js application, you can see a list of routes that are generated.
For this website, the output looks like this:
Route (app) Size First Load JS
┌ ○ / 4.57 kB 95.8 kB
├ ○ /_not-found 0 B 0 B
├ ○ /about 191 B 91.4 kB
├ ○ /articles 1.79 kB 93 kB
├ ○ /articles/2017/executables-with-node-js 175 B 84.6 kB
├ ○ /articles/2017/supercharging-collaborative-api-development-with-ngrok 175 B 84.6 kB
├ ○ /articles/2018/frontend-gems 175 B 84.6 kB
├ ○ /articles/2018/mass-png-to-jpg-on-windows 175 B 84.6 kB
├ ○ /articles/2018/node-js-advanced-snippets 175 B 84.6 kB
├ ○ /articles/2019/getting-started-with-webrtc 175 B 84.6 kB
├ ○ /articles/2019/picking-gatsby-as-a-static-site-generator 175 B 84.6 kB
├ ○ /articles/2020/automated-doc-deployments-using-kotlin-and-netlify 175 B 84.6 kB
├ ○ /articles/2020/using-the-factory-design-pattern-with-kotlin 175 B 84.6 kB
├ ○ /articles/2023/how-to-install-docker-using-ansible-on-ubuntu-22-04 849 B 85.3 kB
├ ○ /articles/2024/10-tailwind-classes-i-wish-i-found-earlier 175 B 84.6 kB
├ ○ /articles/2024/10-tailwind-classes-i-wish-i-found-earlier/opengraph-image.png 0 B 0 B
├ ○ /articles/2024/a-comprehensive-guide-to-cloudflare-r2 175 B 84.6 kB
├ ○ /articles/2024/a-comprehensive-guide-to-cloudflare-r2/opengraph-image.jpg 0 B 0 B
├ ○ /articles/2024/a-new-javascript-registry 175 B 84.6 kB
├ ○ /articles/2024/a-new-javascript-registry/opengraph-image.jpg 0 B 0 B
├ ○ /articles/2024/introducing-a-new-consultation-booking-feature 192 B 91.4 kB
├ ○ /articles/2024/replacing-dependabot-with-ncu 175 B 84.6 kB
├ ○ /articles/2024/replacing-dependabot-with-ncu/opengraph-image.jpg 0 B 0 B
├ ○ /articles/2024/understanding-monorepos 175 B 84.6 kB
├ ○ /articles/2024/understanding-monorepos/opengraph-image.jpg 0 B 0 B
├ ○ /opengraph-image.png 0 B 0 B
├ ○ /projects 192 B 91.4 kB
├ ○ /sitemap.xml 0 B 0 B
├ ○ /thank-you 175 B 84.6 kB
├ ○ /uses 192 B 91.4 kB
└ ○ /work-with-me 192 B 91.4 kB
+ First Load JS shared by all 84.4 kB
├ chunks/69-8f09a332f747eb9e.js 29.1 kB
├ chunks/fd9d1056-880132a998785323.js 53.4 kB
└ other shared chunks (total) 1.89 kB
Quite a long list, and it's only getting bigger and bigger as I write more articles or add more pages. The good thing is that we can use this list to generate a sitemap and help search engines to discover the content of this website as it grows.
Generating a sitemap
We need a script (I prefer TypeScript) that will generate a sitemap based on the routes that are generated during the build process.
The file name is sitemap.ts
and it will be placed in src/app
directory.
Basically this is a naming convention that Next.js uses to generate the end result so please make sure to follow the naming convention as well as the directory placement.
The script will look like this:
import path from 'path';
import fs from 'fs';
const extensions = ['tsx', 'mdx'];
const baseUrl = 'https://jimfilippou.com';
const baseDir = 'src/app';
function getRoutesFromDir(fullPath: string, prefix = ''): string[] {
const entries = fs.readdirSync(fullPath, { withFileTypes: true });
return entries.reduce((routes, entry) => {
if (entry.isDirectory()) {
const newPrefix = `${prefix}/${entry.name}`;
if (extensions.some((ext) => fs.existsSync(path.join(fullPath, entry.name, `page.${ext}`)))) {
routes.push(newPrefix);
}
// Recursively get routes from subdirectories
const subDir = path.join(fullPath, entry.name);
return routes.concat(getRoutesFromDir(subDir, newPrefix));
}
return routes;
}, [] as string[]);
}
function getRoutes() {
const fullPath = path.join(process.cwd(), baseDir);
const routes = getRoutesFromDir(fullPath);
return routes.map((route) => {
return {
url: `${baseUrl}${route}`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1.0,
};
});
}
export default function sitemap() {
return getRoutes();
}
Now what?
First of all, make sure to change the baseUrl
to your own website URL.
Then, you don't have to worry about running the script manually because Next.js will do it for you on build time.
So when you build your Next.js application, the script will be executed (via Node.js) and the sitemap will be generated in the root directory of your website.
If for whatever reason you want to check the sitemap.xml
file, you can run npm run build
and it's going to be inside the build output directory (usually a folder called out
).
If you don't like what you see, you can always modify the script to fit your needs.
What is the benefit of this script over others?
I have found other scripts online that are not going deep into subdirectories and didn't have the flexibility I needed.
This script is using recursion
to get all the routes recursively from the src/app
directory, then it generates a sitemap with the following properties:
url
: The full URL of the route.lastModified
: The date when the route was last modified.changeFrequency
: How frequently the route is likely to change.priority
: The priority of the route relative to other routes on the site.
You do have the flexibility to change anything you want in the script, but I think it's a good starting point.
Conclusion
Thanks for reading guys and gals. I hope you found this blog post useful and you learned how to generate a sitemap for a Next.js application. Leave your feedback via e-mail at [email protected], I would love to hear from you.