Software Engineer / Product Thinker
← Back

How to create a Sitemap in Nuxt Content

October 11, 2023

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.