EDOARDO CASELLA

Freelance web developer


Blog > Localized meta tags with SvelteKit SSR

Localized meta tags with SvelteKit SSR

Information technology Svelte SvelteKit
Posted Thu Nov 25 2021
Updated Sat Jan 13 2024
The solution shown on this page is applicable to SvelteKit but the technique can be performed on any other frontend framework.


Since PWAs started to be popular, we, frontend developers, have allowed ourselves the luxury of many advantages, but we have also run into some annoying problems, especially related to the needs of SEO. The situation becomes complicated when, in addition to simple data, we also want to favor the indexing of the translations of the contents, especially the super-mega-famous meta tags. I have developed many PWAs but I have never had the opportunity to deal with this type of problem, since many of them were intended for private interaction.

Well, as many already know, during server side rendering the objects exposed by the client are not accessible, objects like the "localStorage", or the "window" itself, which is why it is not possible for us to recover the personal preferences of a user (and the crawler can't as well) because this data is stored in the client. This forces us to render the data in a default language, which will be the only language in which that specific page will be indexed. The translated data in another language can eventually be requested later, but on client side rendering, which means that it will not be indexed.

However, when a customer asked for the possibility of indexing the translations of meta tags as well, I thought for a while before recovering an ancient esoteric solution from the drawer: the language slug.

The trick is to parameterize the address, the good old trick is to include the language slug in the internet address we are requesting. This is an information that the server can know and act consequently. For instance:

www.myssrsite.com/en

Note the last part of the address, /en, stands for the language in which we are requesting a certain content, like an "about page":

www.myssrsite.com/en/about
or 
www.myssrsite.com/it/about

To get this kind of router organization with SvelteKit, we just need to add a super route on top of all the others representing the language parameter, something like:

...
routes >
  [lang] >
    about.svelte  
    home.svelte
    ...

In the routes folder I created the sub-folder "[lang]" (following the instructions given in the official guide for dynamic pages), under which I have created the about.svelte page and in which I will create all the routes of my project that I want to index in different languages.

Now I'm halfway to solving the problem, in fact I have to find a way to use this parameter to make the solution effective.

SvelteKit provides the "context module" script, in which we usually find the usefulness of the "load" function. The "load" function is a special function designed for the purpose of manipulating information in SSR. The code contained in it is executed before the component itself is made available, which is why it helps us to solve the problem just presented.


Let's say for example that I have a website whose default language is Italian. But I want to index the english version as well.


Our intent is to download the content of the about page in English. Which is why we will ask for the address myssrsite.com/en/about where we have prepared the following instructions.

<script context="module">
export async function load({ page }) {
   
   // I get the language param [lang] through the object "page"
   const lang = page.params.lang;
   
   // I fetch the about API, for instance
   // appending the query param "lang", or whatever your endpoint need
   const res = fetch('myssrsite.com/api/about?lang=' + lang);
   
   // ... then return the result
   if(res.ok) {
    return {
     props: { aboutPage: await res.json() }
    }
   }
}
</script>

That's it! This way we have informed the server of a parameter that can be exploited to obtain the data immediately translated into a specific language. Then data will be available to the regular Svelte script and rendered in the head and/or body of the page.

Then we export the property which was created during the SSR with a regular script tag:

<script>
 export let aboutPage; 
</script>

and render the data:

<svelte:head>
 <title>{ aboutPage.title }</title>
 <meta name="description" content="{ aboutPage.description }" />
</svelte:head>

If you want to know how to share this data among the various components during the SSR, read this guide.