Nuxt - how to create a sitemap Google Search will like!

Search for a command to run...

No comments yet. Be the first to comment.
I recently added a YouTube video to my page and it negatively affected my website Page Insights (PI) performance. One of the tips PI provided was to provide facades for the Youtube video links, to avoid loading them directly. I implemented it and it ...

Strapi CMS is an open-source content management system I implemented recently in one of my projects. Some parts of integrating Strapi with Nuxt were not well described, so I decided to share solutions I've done on my blog. Why preview? I work with so...
When you are about to embark on a journey to become a full stack developer, or you want to build web services on your own, there are many choices to be made. A lot of them depend on the services you want to build and your current knowledge. When I to...

Hello!
I recently implemented Sitemap in Nuxt, including i18n (internationalization) and dynamic links. I didn't find any tutorial online about implementing a sitemap combining these options, so I hope this guide will help some other devs.
@nuxtjs/i18n provides functionality that enables having i18n in your app. I will assume that you already have some setup for it. That's how it looks for me:
export default defineNuxtConfig({
//..Other config sections
modules: [
//..Other modules
['@nuxtjs/i18n',{
locales: [
{ code: 'en', iso: 'en-US' },
{ code: 'sv', iso: 'sv-SE' },
// Add more locales here
],
defaultLocale: 'en',
strategy: 'prefix_except_default',
vueI18n: './i18n.config.ts',
detectBrowserLanguage: false,
}
],
]
})
locales specifies which locales you want to have in your sitemap.
strategy meaning is explained in the documentation.
detectBrowserLanguage is annoying, I recommend setting it to false.
vueI18n is my file with my messages, it looks approximately like this
export default defineI18nConfig(() => ({
legacy: false,
locale: 'en',
messages: {
// ADD COMA AFTER EACH LINE BELOW, OTHERWISE IT CRASHES WITH UNSPECIFIC ERROR
en: {
// NAVBAR
navbarHome: 'Home',
..., //your other messages
},
sv: {
// NAVBAR
navbarHome: 'Hem',
..., //your other messages
},
}
}))
I chose to use @nuxtjs/sitemap
Part of my setup in nuxt.config.ts that focuses on sitemap module looks like this:
export default defineNuxtConfig({
//..Other config sections
modules: [
//..Other modules
['@nuxtjs/sitemap',
{
sources: ['/api/sitemap'],
xslColumns: [
{ label: 'URL', width: '50%' },
{ label: 'Last Modified', select: 'sitemap:lastmod', width: '25%' },
{ label: 'Hreflangs', select: 'count(xhtml:link)', width: '25%' },
],
}],
],
})
xslColumns is defining how the sitemap is presented, check the documentation to learn more.
The sources are actually where we specify the dynamic links that we want to add to our sitemap. More info in the documentation.
In the contents constant, I receive a list of all the articles that I have in my backend. Then I proceed to extract slugs for all of them and prepare them
My file looks approximately like this, it's not perfectly optimized but should give you an idea:
export default defineSitemapEventHandler(async () => {
// get all the slugs
async function fetchContents() {
try {
const contents = await $fetch<ApiResponseContent>('link_to_api_endpoint');
// Extract the slugs.
const sitemapEntries = contents.data.map(content => {
const mainSlug = content.attributes.slug
// Some blog articles will have localizations, if not return []
const localizationEntries = content.attributes.localizations?.data.map(localization => {
return {
link: [
{
hreflang: localization.attributes.locale,
href: localization.attributes.slug
},
{
hreflang: 'x-default', // Assuming 'en' is the default locale
href: content.attributes.locale == 'en' ? mainSlug : localization.attributes.slug}
}
]
};
}) || [];
return [
{
loc: mainSlug,
_sitemap: content.attributes.locale == 'en' ? "en-US" : "sv-SE",
link: [
{
hreflang: content.attributes.locale,
href: mainSlug
},
...localizationEntries.map(entry => entry.link).flat()
]
},
];
});
return sitemapEntries;
} catch (error) {
console.error("Error fetching pages:", error);
return [];
}
}
// Call the function and handle the slugs
const slugsContents = await fetchContents()
const allSlugs = [...slugsContents]
const flattenedSlugs = allSlugs.flat();
return [
...flattenedSlugs.map(content => asSitemapUrl({
loc: content.loc,
alternatives: content.link,
_sitemap: content._sitemap
}))
]
This way we end up with a list of slugs in an asSitemapUrl format. We specify the link (loc) of every item and its alternatives. Each item WITHOUT translation should have 1 alternative (the link to the item itself), and each item WITH translation should have 3 alternatives (the item's link, its other language version link, and its default language).
Some slugs should end up in an English sitemap, some in the Swedish one. You specify in which sitemap they should be by specifying the _sitemap parameter. Make sure you use the correct name that i18n is using for your locales.
If you follow my description, you should be able to see the resulting sitemaps at yourwebsitelink.com/sitemap.xml after starting your application. You can then proceed to change the look of each of the sitemaps by editing the xslColumns parameter.
Hope this guide gave you some sense of direction on how to implement i18n and dynamic links in the sitemap in Nuxt. If you have any questions feel free to reach out to me, I'll be happy to help!
Cheers!
Bart