DEV Community

Cover image for Implement Protected Routes in NextJS
Shubham Verma
Shubham Verma

Posted on • Updated on • Originally published at blogs.shubhamverma.me

Implement Protected Routes in NextJS

Protecting Routes from unauthenticated users is a crucial part of any app.

In this blog, I'll show you exactly how to do that with your NextJS pages using Higher-Order Components. [1]

There can be several ways of authenticating a user like using cookies or JWT tokens.[2]

I'll be using JWT token as an example, where the accessToken is stored in the localStorage

Let's consider a page "/dashboard". This page should be only accessed by authenticated users

In our Dashboard.jsx

// pages/dashboard.jsx
import withAuth from "HOC/withAuth.js";
const Dashboard = ({ user }) => {
  return (
    <div>
      <h1>Dashboard</h1>
      <h2>{user.name}</h2>
    </div>
  );
};

export default withAuth(Dashboard);
Enter fullscreen mode Exit fullscreen mode

Notice that we are importing withAuth.jsx and exporting the page by passing it as an argument. That is all we need to do for our pages.


In our withAuth.jsx

I'll show you two methods of implementations:

  • Method 1: We don't verify the token
  • Method 2: We verify the token

Method 1: (We don't verify the token)

// HOC/withAuth.jsx
import { useRouter } from "next/router";
const withAuth = (WrappedComponent) => {
  return (props) => {
    // checks whether we are on client / browser or server.
    if (typeof window !== "undefined") {
      const Router = useRouter();

      const accessToken = localStorage.getItem("accessToken");

      // If there is no access token we redirect to "/" page.
      if (!accessToken) {
        Router.replace("/");
        return null;
      }

      // If this is an accessToken we just render the component that was passed with all its props

      return <WrappedComponent {...props} />;
    }

    // If we are on server, return null
    return null;
  };
};

export default withAuth;
Enter fullscreen mode Exit fullscreen mode

Method 2: We need to verify the token.

// HOC/withAuth.jsx
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
// the below function could be any of your custom implementation for verifying the token. I've added it as means of explanantion
import verifyToken from "services/verifyToken";

const withAuth = (WrappedComponent) => {
  return (props) => {
    const Router = useRouter();
    const [verified, setVerified] = useState(false);

    useEffect(async () => {
      const accessToken = localStorage.getItem("accessToken");
      // if no accessToken was found,then we redirect to "/" page.
      if (!accessToken) {
        Router.replace("/");
      } else {
        // we call the api that verifies the token.
        const data = await verifyToken(accessToken);
        // if token was verified we set the state.
        if (data.verified) {
          setVerified(data.verified);
        } else {
          // If the token was fraud we first remove it from localStorage and then redirect to "/"
          localStorage.removeItem("accessToken");
          Router.replace("/");
        }
      }
    }, []);

    if (verified) {
      return <WrappedComponent {...props} />;
    } else {
      return null;
    }
  };
};

export default withAuth;
Enter fullscreen mode Exit fullscreen mode

Footer

  1. React Higher-Order Components

  2. User authentication in NodeJS


Wasn't that easy!

I hope this blog helped you. If you got any queries or feedback then let me know 😀

Oldest comments (35)

Collapse
 
adefizzy045 profile image
Adeniji Adefisayo

Thank you for the post. however you might need to check this

if (typeof window === "undefined") {....}

I think it should be if (typeof window !== "undefined") {...}

because you are checking to know if you are on the browser.

Collapse
 
kirasiris profile image
Kevin Uriel Fonseca

Yes, I think that was a mistake by him but overall this post is good and it helped me a lot. I'm sure this post will keep helping a lot of people later on.

Collapse
 
shubhamverma profile image
Shubham Verma

Thank you Kevin! Glad it helped you

Collapse
 
shubhamverma profile image
Shubham Verma

yes, that was a mistake. Thanks for notifying it, I'll fix it

Collapse
 
ankurpata profile image
Ankur Pata

Will not work with a page which has getInitialProps or getServerSideProps()

Collapse
 
shubhamverma profile image
Shubham Verma

I havent tried but thanks for notifying

Collapse
 
darshija profile image
darshija

Hey, Can you explain why and any solution for this issue?

Collapse
 
surafelgetachew profile image
Surafel Getachew

This helped a lot and thank you, but there is an issue in the console it there is a warning saying react-dom.development.js?61bb:67 Warning: Expected server HTML to contain a matching

in .
Collapse
 
shubhamverma profile image
Shubham Verma

Hey, glad the article helped you.
At the time of writing it, I didn't face any such issue.

On searching, I stumbled upon this
github.com/vercel/next.js/discussi...

I am not sure which above methods you implemented but this might solve your issue

My solution might not be perfect but you could tweak around it from the above GitHub discussion

Collapse
 
sakib25800 profile image
Sakibul Islam

Thank you so much! I was looking for a way to do protected routes for so long!

Collapse
 
shubhamverma profile image
Shubham Verma

Glad it helped!

Collapse
 
hellodemola profile image
Ademola Onasoga • Edited

Thanks, this was a major save. I have one issue though, when I navigate through the protected routes, there seems to be a short flash before content apears using the verify token.

Collapse
 
shubhamverma profile image
Shubham Verma

Glad it saved you!
yes those flashed are actually the useEffect running on mount and until then there nothing to render.
You can maybe add Loading components or something to indicate that the token is getting verified

Collapse
 
hellodemola profile image
Ademola Onasoga

Oh. Guessed as much. Thanks for your prompt response. You're awesome!

Collapse
 
lingyiaoyang profile image
lingyiaoyang

verifytoken is a dependencies?

Collapse
 
shubhamverma profile image
Shubham Verma

No, verifyToken is a function, that you would implement to verify your JWT token.

Collapse
 
okumujustine profile image
okumujustine

Very helpful 👏

Collapse
 
segunade profile image
Segun

Thanks for sharing. I found this helpful

Collapse
 
minhhunghuynh1106 profile image
igdev

return (props) => { ... }
What is purpose of "props"? Thanks for your article 😘

Collapse
 
shubhamverma profile image
Shubham Verma • Edited

props is a object that would contain a react component in the form of an object.
So those props are again passed in the <WrappedComp />

You can try console.log(MyReactComponent()) to see the props!

Collapse
 
minhhunghuynh1106 profile image
igdev

If u using getServerSideProps, when you view page source then it not have any content, not friendly SEO 😥 Can u fix that?

Collapse
 
eurowhisper profile image
EuroWhisper

Hello, in case of it not working with getServerSideProps, I believe it doesn't matter. Because search engines can't crawl protected routes anyway (the search engine doesn't have a username/password for your application). Basically, any page you need to pass a login screen in order to view won't be crawled by a search engine spider anyway. In other words, don't wrap any publicly viewable (and therefore search engine crawlable) pages with WithAuth() in order to avoid trouble with SEO. :)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
hoangvu12 profile image
Vu Nguyen

Edit: This won't works

Collapse
 
saravanakumar645 profile image
Saravana Kumar . V

So I have tried this . Eventually an issue arose which was the type of window is undefined on rendering and it returns null . so nothing gets loaded . My screen is empty and my web app freezes.

Collapse
 
ya7mo profile image
Yahya Aloyoni.

Thanks a lot for this post its very helpful.
i am facing an issue when wrapping my page withAuth, it loads the page then redirects to "/" or whatever my fallback is, is there a way to prevent that from happening.

Collapse
 
shubhamverma profile image
Shubham Verma

You could try removing the Router.replace("/"), by returning some JSX like "Verifyiing" or something

Collapse
 
nahueldev23 profile image
Nahuel

Thanks for the article. I'm using " next ":" ^ 12.0.4 ", and I don't have access to localStorage or cookies, do you have any idea what that might be?

Collapse
 
buka4rill profile image
Ebuka Abraham

localStorage is in the browser. Every modern browser has local storage

Collapse
 
harshmalviya72 profile image
Harsh Malviya

Error: Component definition is missing display name.
I received this error while using the component verifying the accessToken.