Serving a fully localized sitemap with generated routes on nuxt.js
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.