How to create a Sitemap in Nuxt Content

Created by human

Nuxt Content is an amazing tool that makes working with markdown content on Nuxt projects a breeze. Thanks to the power of Nuxt server routes, it's really easy to create a sitemap of our pages.

Let's take a look at the code.

import { SitemapStream, streamToPromise } from 'sitemap';
import { serverQueryContent } from '#content/server';

export default defineEventHandler(async (event) => {
    const docs = await serverQueryContent(event).find();

    const staticSites = [
        {
            _path: '/'
        },
        {
            _path: '/about'
        },
        {
            _path: '/open-source'
        }
    ];

    const sitemapElements = [...staticSites, ...docs];

    const sitemap = new SitemapStream({
        hostname: import.meta.env.VITE_BASE_URL as string
    });

    for (const doc of sitemapElements) {
        sitemap.write({
            url: doc._path,
            changefreq: 'monthly'
        });
    }

    sitemap.end();
    return streamToPromise(sitemap);
});

First of all, let's create a new API route inside api/routes directory by creating a new file called 'sitemap.ts'. Thanks to that, the RRS feed will be available on your-domain.com/sitemap.xml.

Next, inside our defineEventHandler, we have to fetch all articles by using serverQyeryContent:

const docs = await serverQueryContent(event).find();

If you have any other static pages like for example contact or about, you can create an array with those pages:

const staticSites = [
        {
            _path: '/'
        },
        {
            _path: '/about'
        },
        {
            _path: '/open-source'
        }
    ];

And now, we can combine our articles with static pages:

const sitemapElements = [...staticSites, ...docs];

Next up, let's create a sitemap stream by using the utility function from the 'sitemap' package:

const sitemap = new SitemapStream({
    hostname: import.meta.env.VITE_BASE_URL as string
});

Now when we have a sitemap stream, we can iterate over our sitemap elements and write them to the sitemap stream:

for (const doc of sitemapElements) {
    sitemap.write({
        url: doc._path,
        changefreq: 'monthly'
    });
}

Next, we have to fetch all the documents we want to include inside our RRS feed. We can make use of the serverQueryContent function from '#content/server':

const docs = await serverQueryContent(event)
    .sort({ date: -1 })
    .where({ _partial: false })
    .find();

Next up, we have to iterate over our docs and put every one of them to our previously created feed object:

for (const doc of docs) {
    feed.item({
        title: doc.title ?? '-',
        url: `${BASE_URL}${doc._path}`,
        date: doc.date,
        description: doc.description
    });
}

Finally, we have to end the stream and return our sitemap stream as a Promise by using the streamToPromise function from the 'sitemap' package.

sitemap.end();
return streamToPromise(sitemap);

Alternatively, if you would like to prerender the sitemap all you have to do is to specify prerender routes in nuxt config:

export default defineNuxtConfig({
    // ...
    nitro: {
        prerender: {
            routes: ['/sitemap.xml']
        }
    },
});

And that's all! I love how simple this solution is thanks to Nuxt server routes and nitro prerendering.