Redirects in Next.js, the Best Way

🚨 If you're reading this, this post is really old and I have not been using Next.js for many years now, check in the Next.js docs how to do redirects, don't follow this post.

When working with Next.js is super common to reach the point you need to redirect the user to another page, maybe because the user tried to access a private page or the user tried to access an old page.

This could be done in multiple ways, the most popular is to use an HTTP Redirect to achieve this, or Router.replace if the page is accessed client-side.

While that works just fine is not ideal, for the first render the user will need an extra request to the server to get the correct page, but it turns out you already know the page the user is going to request since you are setting the HTTP Redirect, why not then render the correct page at straightaway?

Server-Side Code

This way to set the redirect involves both Server and Client-side code, let's start with the server-side code. Let's say we have two pages, a /login and a /private page, both in our pages directory like this.

[
  {
    "type": "folder",
    "name": "pages",
    "children": [
      { "type": "file", "name": "login.js" },
      { "type": "file", "name": "private.js" }
    ]
  }
]

In our private page we want to render our login page if the user is not logged in, let's say we know if the user is logged in because it has a certain cookie. We could validate the logged-in state in getInitialProps.

PrivatePage.getInitialProps = async context => {
  const { sessions } = readCookies(context.req);
  if (!session) return { loggedIn: false };
  // the code required for your private page
};

Note: readCookies is a fake function that should read the cookies from the request headers and return an object with them.

Now in our PrivatePage component, we can render the login directly.

import dynamic from "next/dynamic";
const LoginPage = dynamic(() => import("./login"));
// more imports here

function PrivatePage({ loggedIn, ...props }) {
  // some hooks here that need to be before the condition
  if (!loggedIn) return <LoginPage />;
  // the JSX the private page will render
}

// define getInitialProps here

export default PrivatePage;

With this when the user access /private and doesn't have the session cookie, it will instead receive the HTML of the login page.

Note: We used a dynamic import to avoid loading the code of LoginPage if it's not going to be used. This will save the user a few kB.

Client-Side Code

Let's go to the Client-Side part of our redirect, the user accessed /private and received the login page HTML, that's great but the user still sees /private in their browser. Let's fix that.

Next comes with a module called next/router which lets us change the route programmatically, we could use that to navigate to another page without requiring the user to click a link.

Let's add an effect in our PrivatePage to change the URL.

// more code here

function PrivatePage({ loggedIn, ...props }) {
  // some hooks here that need to be before the condition
  React.useEffect(() => {
    if (loggedIn) return; // do nothing if the user is logged in
    Router.replace("/private", "/login", { shallow: true });
  }, [loggedIn]);

  if (!loggedIn) return <LoginPage />;
  // the JSX the private page will render
}

// more code here

This effect will do the trick, what that is doing is, first, validate if the user is logged in to do nothing, but if the user is not logged in it will replace the current URL with /.

The function Router.replace receives the href which is the actual route inside Next (aka the page) and the as which is the route we want to show to the user. Those two let use tell Next.js to use our already loaded PrivatePage but disguise it as /login in the browser.

The shallow: true tell Next to do not call getInitialProps, combine with the other two will make Next only change the URL but not doing anything else, this means technically the user is still on PrivatePage which is rendering LoginPage.

When the user log in into the application it will be redirected back to /private but this time without the shallow: true and that will cause the getInitialProps to be executed again so it will see this time it has the session cookie and it will continue the normal flow of our PrivatePage.

Final Words

This is the best approach to implement redirects using Next, I learned it while I was working at ZEIT the creators of the framework and is how I implemented a few redirects back there.