The amazing SEO powers of Remix

Chris Bongers - May 6 '22 - - Dev Community

All modern frameworks focus more and more on SEO and how to make it easier for the developers to incorporate this into their websites.

SEO stands for Search Engine Optimization and comes down to how well you optimize your website for the search engines.

Note: Check out these five tags to get started with SEO.

Where does Remix render SEO tags

Remix has a super nifty hook to set your Metadata, and it all starts in the root.tsx file.

You can find the basic meta setter there in the form of this function:

export const meta: MetaFunction = () => ({
  charset: 'utf-8',
  title: 'Remix Notes',
  viewport: 'width=device-width,initial-scale=1',
});
Enter fullscreen mode Exit fullscreen mode

As you can see, we only set some basics here. We'll dive a bit deeper into changing this in follow-up sections.

Then in the render below we use the <Meta /> element to render the actual section like this:

export default function App() {
  return (
    <html lang='en' className='h-full'>
      <head>
        <Meta />
        <Links />
      </head>
      ...
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

The cool part about this setup is that we can use this MetaFunction from everywhere in our app.

When we inspect our source code for any of our pages, we should at least see the following.

<head>
  <meta charset="utf-8" />
  <title>Remix Notes</title>
  <meta content="width=device-width,initial-scale=1" name="viewport" />
</head>
Enter fullscreen mode Exit fullscreen mode

Setting the meta props on a single page

Let's take our Pokémon example as the baseline.

Open up the routes/pokemon/index.tsx file and let's add the meta function there:

export const meta: MetaFunction = () => ({
  title: 'The complete Pokémon list',
  description: 'This is the list with all existing Pokémon.',
});
Enter fullscreen mode Exit fullscreen mode

You might have spotted we added a description, which we didn't use before.
This is not an issue for Remix, as it will simply add it to this page alone.

It results in the following HTML output for this /pokemon page.

<head>
  <meta charset="utf-8" />
  <title>The complete Pokémon list</title>
  <meta content="width=device-width,initial-scale=1" name="viewport" />
  <meta
    content="This is the list with all existing Pokémon"
    name="description"
  />
</head>
Enter fullscreen mode Exit fullscreen mode

Dynamic SEO tags in Remix

We used some static tags until now, where we define the strings we want to set.

But what happens if we want to add dynamic SEO tags on our single Pokémon page?

Remember how we used the loader function on this single page?
We can use that returned data in our metafunction by simply passing it.

export const meta: MetaFunction = ({
  data,
}: {
  data: LoaderData | undefined,
}) => {
  if (!data) {
    return {
      title: 'Pokémon not found',
      description: 'We could not find this Pokémon',
    };
  }

  const name = data.pokemon.name;
  return {
    title: `This is the amazing ${name}`,
    description: `We caught the Pokémon with the name: ${name}`,
  };
};
Enter fullscreen mode Exit fullscreen mode

Here we get the data property that our loader provided.
We can then determine if the data is available and even add a fallback for when we can't find it.

Let's say we open the /pokemon/charizard page, we then get the following meta tags:

<head>
  <meta charset="utf-8" />
  <title>This is the amazing charizard</title>
  <meta content="width=device-width,initial-scale=1" name="viewport" />
  <meta
    content="We caught the Pokémon with the name: charizard"
    name="description"
  />
</head>
Enter fullscreen mode Exit fullscreen mode

Perfect! We made it dynamic now.

Remix SEO options

So far, we only touched on some very basic SEO tags we can set with Remix, but there are tons we can set using this metafunction.

We can use all meta tags and even set our custom ones if you ever need to.

To find a complete list of all meta tags, visit the following website: Meta tags overview.

For an example of something we could set:

export const meta: MetaFunction = () => {
  return {
    charset: 'utf-8',
    description: 'Welcome to our Remix app',
    keywords: 'Remix,app',
    'twitter:image': 'https://remix.app/social.png',
    'twitter:card': 'summary_large_image',
    'twitter:creator': '@DailyDevTips1',
    'twitter:site': '@DailyDevTips1',
    'twitter:title': 'Remix app',
    'twitter:description': 'Chris created this Remix app, check it out',
    custom: 'Something custom you like.',
  };
};
Enter fullscreen mode Exit fullscreen mode

Which would result in the following HTML:

<head>
  <meta charset="utf-8" />
  <meta content="Welcome to our Remix app" name="description" />
  <meta content="Remix,app" name="keywords" />
  <meta content="https://remix.app/social.png" name="twitter:image" />
  <meta content="summary_large_image" name="twitter:card" />
  <meta content="@DailyDevTips1" name="twitter:creator" />
  <meta content="@DailyDevTips1" name="twitter:site" />
  <meta content="Remix app" name="twitter:title" />
  <meta
    content="Chris created this Remix app, check it out"
    name="twitter:description"
  />
  <meta content="Something custom you like" name="custom" />
</head>
Enter fullscreen mode Exit fullscreen mode

I must say, I'm pretty impressed with how easy Remix makes it to set these SEO properties out of the box.

You can also look at my source code on GitHub.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .