7-Dec

React

Create a useful link abstraction in Next.js

The internal link abstraction with Next.js less than perfect. Let's create a better one!

2 min read

·

By Kristofer Selbekk

·

December 7, 2021

I love Next.js. It's a great framework for creating fast websites with my favorite technologies in the world.

I love the way they support APIs. I love how they let me structure things the way I want. But I hate the way their internal linking API works! And you should too.

Luckily, fixing it is pretty simple. In this short article, we're going to build an abstraction on top that makes stuff… just work.

How does it usually work?

Just to recap, this is what you typically have to do in order to link to another site with Next.js.

Here's a regular link:

<a href="https://some.link">Click me</a>

This works fine for external links, but how about internal links? That is – link to another page on your site. Well, it looks like this:

import Link from 'next/link';

// … later
<Link href="/">
  <a>
    Click me
  </a>
</Link>

If you want to use a custom component as your link component – like from your component library – you had to add the passHref link as well.

import Link from 'next/link';
import ThemedLink from 'component-lib/link'

// … later
<Link href="/" passHref>
  <ThemedLink>
    Click me
  </ThemedLink>
</Link>

Gah…

A better Link

Dealing with all of that code just to add a good ol' link is just too much. Let's create our own component – let's call it TextLink:

import NextLink from 'next/link';

const TextLink = ({ href, children }) => {
  return <a href={href}>{children}</a>;
}

This works well for external links – let's add support for internal links as well!

import NextLink from 'next/link';

const TextLink = ({ href, children }) => {
  const isInternalLink = href?.startsWith('/');
  if (isInternalLink) {
    return (
      <NextLink href={href}>
        <a>
         {children}
        </a>
      </NextLink>
    );
  }
  return <a href={href}>{children}</a>;
}

This works fine – let's add our custom text component as well.

import NextLink from 'next/link';
import ThemedLink from 'component-lib/link';

const TextLink = ({ href, children }) => {
  const isInternalLink = href?.startsWith('/');
  if (isInternalLink) {
    return (
      <NextLink href={href} passHref>
        <ThemedLink>
         {children}
        </ThemedLink>
      </NextLink>
    );
  }
  return <ThemedLink href={href}>{children}</ThemedLink>;
}

Now, we can use our fancy link component wherever we want!

<TextLink href="/profile">My profile</TextLink>
<TextLink href="https://github.com/selbekk">
  Github
</TextLink>
<TextLink href="mailto:kristofer@selbekk.io">
  E-mail
</TextLink>

If you want, you can automatically add the rel="noopener noreferrer" prop automatically, as well for external links. And spread all extra props, so it's super flexible:

import NextLink from 'next/link';
import ThemedLink from 'component-lib/link';

const TextLink = ({ href, children, ...rest }) => {
  const isInternalLink = href?.startsWith('/');
  if (isInternalLink) {
    return (
      <NextLink href={href} passHref>
        <ThemedLink {...rest}>
         {children}
        </ThemedLink>
      </NextLink>
    );
  }
  return (
    <ThemedLink 
      href={href} 
      rel="noopener noreferrer"
       {...rest}
    >
      {children}
    </ThemedLink>
  );
}

And that's it!

I hope you copy-paste this component into your app, and get on with your Next.js app :)