Dealing with links in Next.js
Tags: react, javascript, nextjs
Published: 2020-01-09 14:11:53

---
title: Dealing with links in Next.js
published: true
date: 2020-01-02 19:30:35 UTC
tags: react,javascript,nextjs
canonical_url: https://blog.logrocket.com/dealing-with-links-in-next-js/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/cqbcrmi05kwahi0thv7c.jpeg
---
**Written by [Radoslav Stankov](https://blog.logrocket.com/author/radoslavstankov/)**✏️

[Next.js](https://nextjs.org) is an excellent tool for building web applications with React. I kinda see it as [Ruby on Rails](https://rubyonrails.org/) for React applications. It packs a lot of goodies.

One of those goodies is that it handles routing for you.

However, over the years, I have used various routing libraries — a couple versions of [react-router](https://reacttraining.com/react-router/), [found](https://github.com/4Catalyzer/found), [Navi](https://frontarm.com/navi/en/), and now Next.

Often, I had to switch libraries or update react-router, which at every major version is like a new library. Because of this, I got into the habit of isolating routing from the rest of my application.

In this article, I’m going to explain two of my techniques for isolating routing in your application. I use Next as an example, but they can be applied to pretty much all routing libraries:

- Use a custom `Link` component
- Have all paths in a single file

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Technique 1: Custom `Link` component

My first technique is wrapping the `Link` component. Every routing library has a similar component; it is used instead of the `<a>` tag. When clicked, it changes the URL without a full page redirect, and then the routing handles loading and displaying the new page.

In almost all of my projects, I use my own component named `Link`. This component wraps the underlying routing library `Link` component.

Next has a similar [`Link`](https://nextjs.org/docs/api-reference/next/link) component. Its interface is a bit different than that of the others, but it functions in the same way:

```jsx
<Link href="/about">
  <a>About</a>
</Link>
```

I understand why they designed it this way. It is quite clever; it uses [`React.cloneElement`](https://reactjs.org/docs/react-api.html#cloneelement) internally. You can check its code [here](https://github.com/zeit/next.js/blob/canary/packages/next/client/link.tsx). However, it is a bit cumbersome for my taste. It adds a lot of visual destiny to your pages.

This alone can be a good enough reason to wrap a component. In this case, however, I have even bigger reasons. Say I want to migrate out of Next to something like [Gatsby](https://www.gatsbyjs.org/). I’d have to change a lot of code structure; it won’t just replace imports from `next/link` to `gatsby/link`.

Here is how a wrapped version of `Link` is going to work:

```jsx
import * as React from 'react';
import Link from 'next/link';

// allow this component to accept all properties of "a" tag
interface IProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
  to: string;
  // we can add more properties we need from next/link in the future
}

// Forward Refs, is useful
export default React.forwardRef(({ to, ...props }: IProps, ref: any) => {
  return (
    <Link href={to}>
      <a {...props} ref={ref} />
    </Link>
  );
});
```

> **Note:** I’m using TypeScript for all examples. Code will work without types as well.

This is how it will be used:

```jsx
<Link to="/about">About</Link>
```

The new `Link` component starts quite simple, but over time, you can add more functionality. A good candidate for additions is overwriting the defaults for the library.

In Next 9, [automatic prefetching](https://nextjs.org/blog/next-9#production-optimizations) was turned on by default. This prefetches link contents when they are in the page’s viewport. Next uses a new browser API called [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) to detect this.

This is a handy feature, but it can be overkill if you have a lot of links and pages that are dynamic. It is OK for the static side. Usually, I want to have this for specific pages, not for all of them. Or you might want to prefetch only when the mouse is hovering the link.

Our `Link` component makes it simple to turn this feature off:

```jsx
interface IProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
  to: string;
  prefetch?: boolean;
}

export default React.forwardRef(({ to, prefetch, ...props }: IProps, ref: any) => {
  return (
    <Link href={to} prefetch={prefetch || false}>
      <a {...props} ref={ref} />
    </Link>
  );
});
```

Now imagine if we didn’t have our `Link` component and we had to turn off prefetching for every link.

## Technique 2: Have all paths in a single file

One thing I see people doing in React applications is hardcoding links. Something like the following:

```jsx
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
```

This is very brittle. It is not type-safe, and it makes renaming URLs or changing URL structure hard.

The way I solve this is to have a file named `path.ts` at the root of the project. It looks something like the following:

```jsx
export default {
  about: '/about',
  contact: '/contact',
}
```

This file contains all the routes in my application.

This is how it is used:

```jsx
import paths from '~/paths';

<Link to={paths.about}>About</Link>
<Link to={paths.contact}>Contact</Link>
```

In this way, I can change the routes, and I’m protected from typos.

## Handling dynamic routes in Next

Next 9 was an epic release. Its most significant feature was support for [dynamic route segments](https://nextjs.org/blog/next-9#dynamic-route-segments).

Before that, Next didn’t support dynamic routes like `/products/1` out of the box. You had to use an external package like [next-router](https://github.com/pvtri96/next-router) or use URLs like `/products?id=1`.

The way [d](https://nextjs.org/docs/api-reference/next/link#dynamic-routes)[ynamic routes](https://nextjs.org/docs/api-reference/next/link#dynamic-routes) are handled, we need to pass two props to `Link`:

- `href`: Which file is this in the `pages` folder
- `as`: How this page is shown in address bar

This is necessary because the Next client-side router is quite light and doesn’t know about the structure of your whole route. This scales quite well since you don’t hold complicated routing structures in browser memory, as in other routing systems.

Here is how it looks in practice:

```jsx
<Link href="/products/[id]" as="/product/1">
  <a>Product 1</a>
</Link>
```

This makes dealing with links even more cumbersome. Fortunately, we have our custom `Link` and `paths`. We can combine them and have the following:

```jsx
<Link to={paths.product(product)}Product 1</Link>
```

How is this implemented?

First, we add a function in `paths` that returns both props for the page:

```jsx
export default {
  about: '/about',

  contact: '/contact',

  // paths can be functions
  // this also makes it easier to change from "id" to "slug" in the future
  product(product: { id: string }) {
    return {
      href: '/products/[id],
      as: `/products/${id}`,
    };
  }
}
```

Second, we have to handle those props:

```jsx
interface IProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
  // allow both static and dynamic routes
  to: string | { href: string, as: string };
  prefetch?: boolean;
}

export default React.forwardRef(({ to, prefetch, ...props }: IProps, ref: any) => {
  // when we just have a normal url we jsut use it
  if (typeof to === 'string') {
    return (
      <Link href={to} prefetch={prefetch || false}>
        <a {...props} ref={ref} />
      </Link>
    );
  }

  // otherwise pass both "href" / "as"
  return (
    <Link href={to.href} as={to.as} prefetch={prefetch || false}>
      <a {...props} ref={ref} />
    </Link>
  );
});
```

## Migration story

Before version 9, Next didn’t support dynamic routing. This was a big issue, and I had been using [next-router](https://github.com/pvtri96/next-router) for dynamic routing. It has a central file where you create the mapping from URL to file in the `pages` folder. Its `Link` component works quite differently.

It was a lifesaver before Next 9. But when dynamic routes were added to Next, it was time to stop using the library; it is even in maintenance mode now.

Imagine having a large application with hundreds of links. How much time do you think a migration like this could have taken?

For me, it took less than an hour. I just replaced the code in the `Link` component and changed the dynamic paths to return an object and not a route/params as next-router wanted.

## Conclusion

Those techniques have helped me a lot over the years working with React applications. They are relatively simple but help you to decouple your application from underlying libraries, make your system easy to change, and have type safety.

I hope you find them useful as well. For any questions or comments, you can ping me on [Twitter](https://twitter.com/rstankov).

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [Dealing with links in Next.js](https://blog.logrocket.com/dealing-with-links-in-next-js/) appeared first on [LogRocket Blog](https://blog.logrocket.com).