DEV Community

Alex Sidorenko
Alex Sidorenko

Posted on • Originally published at alexsidorenko.com

Protected routes in Next.js compared to React Router

Coming from React Router, Next.js route authentication patterns can seem confusing. Something you can accomplish in one line with React Router is a bit more complicated in Next.js.

Protected route with React Router:

{isAuthorized && <Route path='/about' ... />}
Enter fullscreen mode Exit fullscreen mode

What is the Next.js equivalent of this code?


Disclaimer

In this article, we will only focus on static rendering authentication. It is the closest concept to the usual React implementation with React Router. If you want to do server-side authentication, check out the corresponding section of the docs. Now, let's move on.


Official documentation

Next.js documentation has this example of the protected route implementation:

// pages/profile.js

import useUser from '../lib/useUser'
import Layout from '../components/Layout'

const Profile = () => {
  // Fetch the user client-side
  const { user } = useUser({ redirectTo: '/login' })

  // Server-render loading state
  if (!user || user.isLoggedIn === false) {
    return <Layout>Loading...</Layout>
  }

  // Once the user request finishes, show the user
  return (
    <Layout>
      <h1>Your Profile</h1>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </Layout>
  )
}

export default Profile
Enter fullscreen mode Exit fullscreen mode

But do you repeat this code for every single page? What if you want to add some additional logic, like user types? Should you repeat this on every page too? Well, not really.


Move user logic to _app.js

To get more control over the routes, we can modify default _app.js.

// pages/_app.js

import { UserContext } from "../components/user";

function MyApp({ Component, pageProps }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    /**
     * Here goes the logic of retrieving a user
     * from the backend and redirecting
     * an unauthorized user
     * to the login page
    */
    setUser(result)
  }, []);

  if (pageProps.protected && !user) {
    return (
      <Layout>Loading...</Layout>
    )
  }

  return (
    <UserContext.Provider value={user}>
      <Component {...pageProps} />
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode
// components/user.js

import { createContext, useContext } from "react";

export const UserContext = createContext(null);

export const useUser = () => {
  return useContext(UserContext);
};
Enter fullscreen mode Exit fullscreen mode

Now we can use getStaticProps to make any page protected.

// pages/profile.js

const Profile = () => {

  return (
    <Layout>
      <h1>Your Profile</h1>
    </Layout>
  )
}

export async function getStaticProps(context) {
  return {
    props: {
      protected: true
    }
  };
}

export default Profile
Enter fullscreen mode Exit fullscreen mode

User types

Let's add user types logic to our _app.js.

// pages/_app.js

function MyApp({ Component, pageProps }) {
  const [user, setUser] = useState(null);

  ...

  if (pageProps.protected && !user) {
    return (
      <Layout>Loading...</Layout>
    )
  }

  if (
    pageProps.protected &&
    user &&
    pageProps.userTypes &&
    pageProps.userTypes.indexOf(user.type) === -1
  ) {
    return <Layout>Sorry, you don't have access</Layout>;
  }

  return (
    <UserContext.Provider value={user}>
      <Component {...pageProps} />
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now we can specify allowed user types for a protected route.

// pages/admin.js

const Admin = () => {

  const { user } = useUser()

  return (
    <Layout>
      <h1>Hi {user.name}</h1>
      <p>Welcome to admin dashboard</p>
    </Layout>
  )
}

export async function getStaticProps(context) {
  return {
    props: {
      protected: true,
      userTypes: ['admin']
    }
  };
}

export default Admin
Enter fullscreen mode Exit fullscreen mode

That's it. Here is a demo. I hope this helps to create protected routes in Next.js for those coming from React Router.


Originally published at alexsidorenko.com

Top comments (0)