React Japan
Localized link tags for SEO in Remix
Steven Sacks
Steven Sacks

Localized link tags for SEO in Remix

There are many aspects to building a site with content localized into multiple languages. To optimize SEO, your page needs link elements that point to the alternate localized versions.

Remix has a links function, however, this function does not include data from the loader. If you have dynamic routes, such as a blog with articles and the url based on the slug /blog/some-article, using the links function won’t work, since you cannot access the dynamic part of the route to generate your localized link tags.

The solution is to use Remix’s meta function, instead. The meta function gets passed the data from the loader, and you can use the tagName property to render a <link> tag. Google’s guidelines state that each language version must list itself as well as all other language versions, so you need to include all the links for all the languages.

Here’s how to do it:

import type {LoaderFunctionArgs, MetaFunction} from '@remix-run/node';
 
export const loader = async ({params}: LoaderFunctionArgs) => {
  const response = await fetch(`/api/blog/${params.slug}`);
  
  return await response.json();
};
 
export const meta: MetaFunction<typeof loader> = ({data}) => [
  {title: data?.title},
  {content: data?.description, name: 'description'},
  {
    href: `https://domain.com/blog/${data?.slug}`,
    hreflang: 'en',
    rel: 'alternate',
    tagName: 'link',
  },
  {
    href: `https://domain.com/ja/blog/${data?.slug}`,
    hreflang: 'ja',
    rel: 'alternate',
    tagName: 'link',
  },
  {
    href: `https://domain.com/fr/blog/${data?.slug}`,
    hreflang: 'fr',
    rel: 'alternate',
    tagName: 'link',
  },
];

Utility function

Since all of your localized pages will need to include these links, you should create a utility function that returns these link tags for you.

// utils.ts
 
export const getLocalizedLinks = (path: string) => [
  {
    href: `https://domain.com${path}`,
    hreflang: 'en',
    rel: 'alternate',
    tagName: 'link',
  },
  {
    href: `https://domain.com/ja${path}`,
    hreflang: 'ja',
    rel: 'alternate',
    tagName: 'link',
  },
  {
    href: `https://domain.com/fr${path}`,
    hreflang: 'fr',
    rel: 'alternate',
    tagName: 'link',
  },
];

And then import that into your route meta functions.

import {getLocalizedLinks} from '~/utils';
 
export const meta: MetaFunction<typeof loader> = ({data}) => [
  {title: data?.title},
  {content: data?.description, name: 'description'},
  ...getLocalizedLinks(`/blog/${data?.slug}`),
];

That’s all there is to it!