Build a static blog with Next.js 14: add the sitemap
September 01, 2024Tested with
-
Ubuntu Linux 24.04 LTS
-
Node.js v20.15.0 LTS
-
Next.js v14.2.4
The complete code of this post is available on GitHub.
In this post we will generate dynamically the sitemap.xml
for our Next.js 14 static blog.
An API to list all pages
We need to create an API that is able to read all the pages of our website and all the posts of our blog. Let's create src/lib/api/Pages.js
and write down this code
// File: src/lib/api/Pages.js
// nodejs
import fs from "fs";
import path from "path";
// custom
import { CONFIG_PAGE_EXTENSIONS } from "./../constants.mjs";
const APP_FOLDER = path.join(process.cwd(), "src", "app");
const Pages = () => {
const allowedPages = CONFIG_PAGE_EXTENSIONS.map((extension) => {
return `page.${extension}`;
});
const normalizeFolderPath = (folderPath) => {
const normalized = folderPath.replace(APP_FOLDER, "");
return normalized;
};
const getPages = async (folder, allowedPages, accumulator) => {
const files = fs.readdirSync(folder);
files.forEach(async (file) => {
if (allowedPages.indexOf(file) !== -1) {
accumulator.push({
path: normalizeFolderPath(folder),
});
return accumulator;
}
const child = path.join(folder, file);
if (fs.lstatSync(child).isDirectory()) {
return getPages(child, allowedPages, accumulator);
}
});
return accumulator;
};
return {
get: async () => {
return await getPages(APP_FOLDER, allowedPages, []);
},
};
};
export default Pages;
The core of this API is the getPages()
function: it is a recursive function that navigates the src/app
folder looking for files named page
with one of the configured extensions (js, jsx, mdx
).
Then it normalize the path (making it relative) through normalizeFolderPath
function.
Using the Pages API to generate the sitemap.xml
We have to create the file src/app/sitemap.js
with this code
// custom
import { SITE_URL } from "@/lib/constants";
import Pages from "@/lib/api/Pages";
const apiPages = Pages();
const sitemap = async () => {
const pages = await apiPages.get();
const lastModified = new Date();
return pages.map((page) => {
return {
changeFrequency: "weekly",
lastModified,
priority: 0.5,
url: `${SITE_URL}${page.path}`,
};
});
};
export default sitemap;
To test if the sitemap has been generated correctly we can open http://localhost:3000/sitemap.xml in the browser and see an XML that looks like this
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.example.com/blog/my-1st-post</loc>
<lastmod>2024-07-23T08:26:02.543Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://www.example.com/blog/my-2nd-post</loc>
<lastmod>2024-07-23T08:26:02.543Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://www.example.com/blog</loc>
<lastmod>2024-07-23T08:26:02.543Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://www.example.com</loc>
<lastmod>2024-07-23T08:26:02.543Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
Now our website has a sitemap that could be submitted to search engines.