5 Ways to Redirect URLs in Next.JS
Tags: nextjs, tutorial, webdev
Published: 2024-01-19 14:30:00

Chances are at some point in the development of a web app or website, you've changed the path to a URL for whatever reason. Maybe you've changed the name of a page, or you've changed the structure of your site. Whatever the reason, you'll need to redirect the old URL to the new one.

In this post we'll explore 5 different methods you can use to redirect URLs in Next.JS, using Pages router, each with their own pros and cons.

> Note that for the purposes of this post, I'm assuming permanent redirects. If you need temporary redirects, you can simply change the `permanent` property to `false` in the first 3 code examples below.

## Method 1: Using next.config.js

The first method I'll outline is probably the simplest, and when it works, the fastest.

I say "when it works" because it's not necessarily supported by all hosts. For example, Vercel of course supports it, but [Netlify doesn't currently appear to](https://github.com/netlify/next-runtime/issues/2359). As for other hosts, you'd need to verify with them, but I'd imagine it varies.

To use this method:

1. If you haven't already, create a `next.config.js` file in the root of your project (at the same level as your `package.json` file).

2. Add the following code to the file:

   ```js
   // next.config.js
   module.exports = {
     async redirects() {
       return [
         {
           source: '/old-url-1',
           destination: '/new-url-1',
           permanent: true,
         },
         {
           source: '/old-url-2',
           destination: '/new-url-2',
           permanent: true,
         },
         etc...
       ]
     },
   }
   ```

That's it! Now, when a user visits `/old-url-1`, they'll be redirected to `/new-url-1`. The same goes for `/old-url-2` and `/new-url-2`, etc.

Setting `permanent` to `true` will make the redirect permanent, and cause a 308 HTTP status code to be returned, which is what you want in most cases. If you need a temporary redirect, you can set `permanent` to `false`, which returns a 307 HTTP status code instead.

This method is super simple, and allows for a lot of options including dynamic path matching and even regex matching.

For full details, check out the [official Next.JS redirects documentation](https://nextjs.org/docs/pages/api-reference/next-config-js/redirects).

## Method 2: Using getStaticProps

Now, I'm including this purely because it's listed in the [official Next.JS documentation](https://nextjs.org/docs/pages/api-reference/next-config-js/redirects), ***but it doesn't actually seem to work!!***

Well, that's not entirely true ... it works amazingly well in development, but doesn't seem to work at all in production. And call me crazy, but isn't production exactly where you would need it to work? 🤔🤨

If you use the `redirect` key in getStaticProps in production, you'll likely get the following error during build time:

> Error: 'redirect' can not be returned from getStaticProps during prerendering

According to user **timfuhrmann** in [this github thread](https://github.com/vercel/next.js/discussions/11346#discussioncomment-3612448), this style of redirect only works if `fallback` is set to `true` or `'blocking'`, but I couldn't get it to work any way I tried. I guess I'm doing something wrong, but the docs make no mention of how to correctly do it, and I couldn't find any other info on it.

So, I'm not entirely sure what the purpose of having it is, and why it's listed in the official documentation. I'm including it here for completeness, and so that in the event you run into this error like I did, you understand it's because **it doesn't seem to work in production**.

> If you know what I'm doing wrong, **and what the docs obviously don't mention**, please let me know in the comments and I'll be sure to update this post.

The code is as follows:

```js
// pages/[slug].js | pages/[...slug].js
export async function getStaticProps({ params }) {
  let redirectUrl = '/';

  if (params.slug.includes('old-url-1')) {
    redirectUrl = '/new-url-1';
  } else if (params.slug.includes('old-url-2')) {
    redirectUrl = '/new-url-2';
  }
  
  return {
    redirect: {
      destination: redirectUrl,
      permanent: true,
    },
  }
}

export async function getStaticPaths() {
  return {
    paths: [
      { params: { slug: 'old-url-1' } }
    ],
    fallback: false,
  }
}
```

> **Note:** if you're using dynamic paths, you'll likely need to export both `getStaticProps` and `getStaticPaths`, whereas for a direct path match, you'll only need `getStaticProps`

Now when a user visits `/old-url-1`, they'll be redirected to `/new-url-1`, but again, **this only seems to work work in development**.

Like **Method 1** however, this method also allows for dynamic path matching and regex matching, so if you only need redirects in dev, this could be a solution. That said, I'd recommend using **Method 1** instead, as it's much simpler and more reliable when supported by your host.

For more info on this method, you can check out the [official Next.JS getStaticProps documentation](https://nextjs.org/docs/pages/api-reference/functions/get-static-props#redirect).

## Method 3: Using getServerSideProps

Unlike `getStaticProps`, `getServerSideProps` ***does*** work in production. So if you need redirects in production, this is a good option, albeit a slower one compared to **Method 1**.

This method uses server side rendering, so while it's not as fast as a static redirect, it's still being done server-side, which can be a plus.

You also don't need to worry about `fallback` or `revalidate` options, as they're not needed here.

The code is quite simple:

```js
// pages/[slug].js | pages/[...slug].js
export async function getServerSideProps({ params }) {
  let redirectUrl = '/';

  if (params.slug.includes('old-url-1')) {
    redirectUrl = '/new-url-1';
  } else if (params.slug.includes('old-url-2')) {
    redirectUrl = '/new-url-2';
  }

  return {
    redirect: {
      destination: redirectUrl,
      permanent: true,
    },
  }
}
```

Again, this method also allows for dynamic path matching, which you would accomplish by using the `params` object, and by placing the code in a file with a dynamic route name, like `[id].js`, `[slug].js` or `[...slug].js`.

## Method 4: Using a middleware.ts or .js file

This method is quite a bit more complicated, but it's also a lot more flexible. Using middleware, you can redirect to any other URL, including external URLs.

You can also use conditional logic, cookies, and you can set the response headers as well.

To use this method, you'll need to create a `middleware.ts` or `middleware.js` file in the root of your project (at the same level as your `package.json` file).

Add the following code to the file:

```ts
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname === '/old-url') {
    return NextResponse.redirect(new URL('/new-url', req.url))
  }
}
```

Or, alternatively, using path matching:

```ts
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(req: NextRequest) {
  return NextResponse.redirect(new URL('/new-url', req.url))
}

export const config = {
  matcher: '/old-url/:path*',
}
```

> **Note:** In my test `middleware.js` file, VS Code is showing an error with the NextResponse import, but it's working fine. Seems to be something not updated as it wants me to keep the file in the pages directory, which is no longer required / supported.
>
> ![VS Code error with NextResponse import](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0r5s7j0jxetq2j6q2r7v.jpg)

An important caveat with this method though, per the Next.JS docs, is that it currently only supports the Edge runtime, and not the Node.js runtime.

Refer to the [official Next.JS middleware documentation](https://nextjs.org/docs/pages/building-your-application/routing/middleware) for the full list of features and options.

## Method 5: Using useEffect

And finally, the last method I'll outline is using `useEffect` in a React component. This method is probably the most flexible, but it's also not really a true redirect.

This method would be somewhat similar to the old school method of using a meta refresh tag in the `<head>` of your HTML document, but instead of using a meta tag, we'll use React's `useEffect` along with Next's `useRouter` to redirect the user to the new URL.

Like the middleware method, we can redirect to an external URL, as well as use conditional logic, cookies, etc.

The code is as follows:

```js
import { useEffect } from 'react';
import { useRouter } from 'next/router';

export default function OldPage() {
  const router = useRouter();

  useEffect(() => {
    router.push('/new-url');
  }, []);

  return (
    <div>
      <h1>Old Page</h1>
    </div>
  );
}
```

---

Well, those were the 5 different methods I wanted to outline in this post. Note that if you're using **App Router** instead of **Pages Router**, you might have different options / methods available to you. 

Hope you learned something new in this post, and that it helped if you got stuck somewhere in your own code. 😀