Protected routes in Next.js compared to React Router
Published: 2021-06-22 09:18:26

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

```jsx
{isAuthorized && <Route path='/about' ... />}
```

What is the Next.js equivalent of this code?

---

## Disclaimer

In this article, we will only focus on [static rendering authentication](https://nextjs.org/docs/authentication#authenticating-statically-generated-pages). 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](https://nextjs.org/docs/authentication#authenticating-server-rendered-pages). Now, let's move on.

---

## Official documentation

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

```jsx
// 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
```

**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`.

```jsx
// 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>
  );
}
```

```jsx
// components/user.js

import { createContext, useContext } from "react";

export const UserContext = createContext(null);

export const useUser = () => {
  return useContext(UserContext);
};
```

Now we can use `getStaticProps` to make any page protected.

```jsx
// pages/profile.js

const Profile = () => {
  
  return (
    <Layout>
      <h1>Your Profile</h1>
    </Layout>
  )
}

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

export default Profile
```

---

## User types

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

```jsx
// 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>
  );
}
```

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

```jsx
// 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
```

That's it. Here is a [demo](https://codesandbox.io/s/adoring-butterfly-ghh67?file=/pages/_app.js). I hope this helps to create protected routes in Next.js for those coming from React Router.

---

Originally published at [alexsidorenko.com](https://alexsidorenko.com/)