Nextjs App Router Automatically Generate Sitemap for Static Page

I’m working on a little programmatic seo side project called speedyonlinetools.com, a website offering a variety of useful calculators and online tools. Since the site is fully static and doesn’t use a database, manually creating a sitemap for every new calculator, category, or tool would become quite annoying as the project grows (I’m aiming to have at least 100 calculators by the end of the year). Not only is it time-consuming, but it’s also easy to forget to update the sitemap, which could lead to missing pages.

We are developers, right? So, we can automate the shit out of it. Since the NextJS app router organizes URLs based on directory names, I need to iterate through all the directories in the /app folder and check for a page.js file in each one. If the file exists, it indicates a valid route that can be included in the sitemap.

So, the following function is doing just that.

function generateSitemap() {
    const baseUrl = process.env.BASE_URL;
    const appDirectory = path.join(process.cwd(), 'app');

    // Recursively get all directories in the app directory that contain a page.js file
    function getDirectoriesWithPageJs(dir, dirList = []) {
        const files = fs.readdirSync(dir);

        files.forEach(file => {
            const filePath = path.join(dir, file);
            if (fs.statSync(filePath).isDirectory()) {
                getDirectoriesWithPageJs(filePath, dirList);
            } else if (file === 'page.js') {
                dirList.push(dir);
            }
        });

        return dirList;
    }

    // Get directories containing page.js files
    const directoriesWithPageJs = getDirectoriesWithPageJs(appDirectory);

    // Create sitemap entries
    const files = directoriesWithPageJs.map(dir => {
            const relativePath = path.relative(appDirectory, dir);
            const urlPath = relativePath.replace(/\([^)]*\)\/?/g, '');
            return `${baseUrl}/${urlPath}`;
        })
        .filter((url, index, self) => self.indexOf(url) === index) // Remove duplicates
        .map(url => ({
            url,
            lastModified: new Date(),
            changeFrequency: 'monthly',
            priority: 0.5
        }));

    return files;
}