Build a static blog with Next.js 14: add the sitemap

September 01, 2024

Tested 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.

References


Did you find this post useful? What about Buy Me A Coffee

A photo of Elia Contini
Written by
Elia Contini
Sardinian UX engineer and a Front-end web architect based in Switzerland. Marathoner, traveller, wannabe nature photographer.