How to add dynamic canonical links in Remix application
Tags: react, canonicallink, seo, remix
Published: 2022-07-09 20:48:35

---
title: How to add dynamic canonical links in Remix application
published: true
date: 2022-07-07 21:04:54 UTC
tags: react,canonicallink,seo,remix
canonical_url: https://aibolik.com/blog/how-to-add-dynamic-canonical-links-in-remix-application
---

> This article was originally written on my personal blog [here](https://aibolik.com/blog/how-to-add-dynamic-canonical-links-in-remix-application).

I rewrote my blog using Remix.run several months ago, but I faced an issue with **canonical links** when I started improving the SEO of my blog. After some research about this, I came to a perfect solution that lets me have **dynamic link** elements that absorb route data. Let me show you how I got to it.

![Two similar white pages in a orange background](https://cdn-images-1.medium.com/max/1024/1*wke4B4SPRlIzvrWQt1T4QQ.jpeg)
_Make sure you add canonical URLs to all of your pages in Remix app. Credits: Table [flatlay photo](https://www.freepik.com/photos/table-flatlay) created by freepik_

### What is a canonical URL

> If you already know what it is and why you need it, jump to the next section.

Simply put, a canonical URL is used by search engines to determine the origin of the content. Suppose your blog post is available through http://, https:// and/or www. URLs. In that case, it is highly recommended to put a canonical URL that follows some structure, so that search engines will mark other pages as duplicates and _give all the credits_ to the original content.

Another popular reason to use canonical URLs is to **syndicate your content**. If you publish your post on your blog and third-party platforms(like medium.com, dev.to, etc.) Google will consider them as [duplicate content](https://moz.com/learn/seo/duplicate-content). It might hurt the ranking of your website but using the canonical URLs you can turn this on its head and get back again _all the SEO credits_ to your website by setting up canonical URLs in third-party platforms. This blog post is not meant to explain everything about canonical URLs, so if you still have questions I highly recommend reading about it [here](https://developers.google.com/search/docs/advanced/crawling/consolidate-duplicate-urls).

> Even if I duplicated this post to Dev.to, I still get all the “SEO credits” to my website because of the canonical URL. Check out the canonical URL link the source of this page.

### The problem with the links in Remix.run

Most likely, you have a template route like `blog.$slug.tsx` in your Remix application. I thought I could add canonical URLs using a [links](https://remix.run/docs/en/v1/api/conventions#links) function, just like this(assuming I return canonicalUrl in the route loader):

```
export const links: LinksFunction = ({ data }) => {

  return [{
    rel: 'canonical', href: data.canonicalUrl,
  }];
}
```

But TypeScript immediately turned me down and didn't accept having an object with data in parameters. It turns out LinksFunction does not have access to data returned by loader and we can't put there any _dynamic_ content.

### Solution: use DynamicLinks

So after some research on the internet about adding dynamic link elements to a page in Remix, I came to a solution using DynamicLinks. So, DynamicLinks is not something that comes from the [Remix.run documentation](https://remix.run/docs) it is rather a utility that can be built using Remix.run capabilities.

> Big thanks to [Sergio](https://twitter.com/sergiodxa) who implemented it in [remix-utils](https://github.com/sergiodxa/remix-utils) Open-Source library.

#### How does DynamicLinks utility works

Let me show you the code and walk you through what is happening:

```
export function DynamicLinks() {
  let links: LinkDescriptor[] = useMatches().flatMap((match) => {
    let fn = match.handle?.dynamicLinks;
    if (typeof fn !== 'function') return [];
    return fn({ data: match.data });
  });

  return (
    <React.Fragment>
      {links.map((link) => (
        <link {...link} key={link.integrity || JSON.stringify(link)} />
      ))}
    </React.Fragment>
  );
}
```

- First of all, we get the all matched routes using the [useMatches](https://remix.run/docs/en/v1/api/remix#usematches) utility hook
- We look for our pre-defined dynamicLinks function in exported [handle](https://remix.run/docs/en/v1/api/conventions#handle) constant of each matched route and call it giving the route data as a parameter(if the function is defined by the route)
- After we collect links from all the matched routes, we simply render them

We can put this component at the root of the Remix app and it will work for all the routes that define dynamicLinks function in exported handle variable.

#### How to use DynamicLinks in Remix app

The usage of the utility is very simple. I define dynamicLinks function in my blog.$slug.tsx module and export it within handle Remix-defined constant:

```
const dynamicLinks: DynamicLinksFunction<LoaderData> = ({ data }) => {
  return [
    {
      rel: 'canonical', href: data.canonicalUrl,
    },
  ];
}

export const handle = {
  dynamicLinks,
};
```

> Note that this code assumes that I return canonicalUrl from my loader function.

And then I just need to render DynamicLinks the component in the root of the Remix application:

```
import { DynamicLinks } from "remix-utils";

export default function App() {

 return (
    <html lang="en">
      <head>
        {/** your head tags */}
        <DynamicLinks />
      </head>
      <body>
        {/** body tags */}
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}
```

This results in having a dynamic canonical URL in all of my blog posts(both client and server-side). E.g.:

```
<link rel="canonical" href="https://aibolik.com/blog/how-to-add-dynamic-canonical-links-in-remix-application"/>
```

That’s it! I was upset at the beginning that LinksFunction does not support loader data(for sure, for some good reason), but I liked how Remix gives the flexibility to implement this kind of utility. There are more examples in the [Remix.run documentation](https://remix.run/docs/en/v1/api/remix#usematches) about the useMatches utility hook, like implementing breadcrumbs for all the nested components.

Thank you for reading this post. If you have any questions please leave comments or tweet tagging me(at [aibolik](https://twitter.com/aibolik_)\_).