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 Word
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.
Top comments (2)
@sergiodxa I tried this on a sample project and noticed something odd. If I go to the PrivatePage while not logged in then it redirects me to the Login page with the right url in the browser (i.e. /login). That is all good.
But once I login in then I am redirected back to PrivatePage but the url in the browser still says /login. Any thoughts on how you might solve that?
This seems like it might get repetitive if you have a lot of private pages. Is there a way to componentize this?
Thanks,
Ben
@sergiodxa Thanks. This looks quite good. Have you published a repo of the full code?