Serving a fully localized sitemap with generated routes on nuxt.js

Deniz Gençtürk
3 min readJun 7, 2020

In the previous post, we looked at tackling both static and dynamic translations using a sanity backend. Now we will generate a localized sitemap to help with SEO as well as use these routes to pass to the nuxt generate command.

Nuxt already takes care of our static routes by looking at the pages folder by default. With the i18n module, the translated endpoints of these routes are also added to our sitemap and generated pages.

Setting up the routes array

To extend our routes we’ll need to pass an array of routes to both the generate and the sitemap configuration options. Since we will be needing these routes in two separate places it’s a good idea to have one function at the beginning of the configuration file and pass the generated array later below in the options.

Below I added the German and Portuguese top-level routes because nuxt wasn’t generating these by default. You can add other static routes that you want to be generated in this same format if nuxt isn’t already generating them for you.

// nuxt.config.jsconst routeGenerator = function() {
const routes = []
return routes
}
const routes = routeGenerator()export default {
...otherConfig,
generate: {
// only pass the urls to the generate function
routes: routes.map(route => route.url)
},
sitemap: {
// pass the sitemap objects as is to the sitemap module
routes
}
}

Adding dynamic routes to the mix

In this example, we will fetch some projects from our sanity backend. Let’s start by changing our routeGenerator function to async and start fetching some data from sanity.

In the example below, I’m using the default _updatedAt field from sanity to add a lastMod to the sitemap, and the imageUrlBuilder from sanity to get the URL of the image I want to use.

Then for each sitemap item, I’m using sanity’s localize function to get the localized versions of the title and caption fields.

// nuxt.config.jsimport { client } from './sanity'
import imageUrlBuilder from '@sanity/image-url'
const builder = imageUrlBuilder(client)
import { localize } from './utils'
const routeGenerator = async function() {
const routes = [{ url: '/de' }, { url: '/pt' }]
const projects = await client.fetch(`*[_type == "project"] | {photo, title, subtitle, slug, "lastMod": _updatedAt}`) // here the title and subtitle fields are localeString fields
for (const { title, subtitle, slug, lastMod, photo } of projects) {
// here we initialize the sitemap item, feel free to customize
const item = {
lastMod,
priority: 0.6,
changefreq: 'yearly',
}
// here we only add the img field if a photo exists, otherwise the builder function throws an error
if (photo) {
item.img = {
url: builder.image(photo).format('webp').url(),
title,
caption: subtitle
}
}
// here we append a separate item to our routes for each language we have.
routes.push(
{
url: `/clients/${slug}`,
...localize(item, ['en', 'en'])
},
{
url: `/de/clients/${slug}`,
...localize(item, ['de', 'en'])
},
{
url: `/pt/clients/${slug}`,
...localize(item, ['pt', 'en'])
}
)
}
return routes
}
export default {
...otherConfig,
generate: {
// only pass the urls to the generate function
routes: routes.map(route => route.url)
},
sitemap: {
// pass the sitemap objects as is to the sitemap module
routes
}
}

This is all the logic needed to get a sitemap item for each language for each dynamic route!

The value of the url field may change depending on your i18n route naming convention. Nuxt i18n by default creates locale routes with the language code at the beginning. If you’ve customized this behavior, you will need to apply this change here in our function as well.

That’s it! Remember to run the nuxt generate command each time you make updates to the projects on sanity, or set-up a webhook to automize this process.

--

--