5 Ways to Redirect URLs in Next.JS

Michael La Posta - Jan 19 - - Dev Community

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. 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:

   // 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...
       ]
     },
   }
Enter fullscreen mode Exit fullscreen mode

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.

Method 2: Using getStaticProps

Now, I'm including this purely because it's listed in the official Next.JS documentation, 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, 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:

// 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,
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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:

// 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,
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

// 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))
  }
}
Enter fullscreen mode Exit fullscreen mode

Or, alternatively, using path matching:

// 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*',
}
Enter fullscreen mode Exit fullscreen mode

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

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 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:

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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. ๐Ÿ˜€

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