Protected routes with React Router v5

Josh Sherman
5 min read
Software Development JavaScript

React Router makes it extremely easy to define routing in your React application, but out of the box doesn’t concern itself with which routes should require authentication and which ones should now.

I’ve solved this problem in a past life, using React Router v4 and looking back at the code we had written, it left quite a bit to be desired. So much so that when I tried to copy pasta the code into a new project of mine, it didn’t work as expected.

Fast forward to after researching and reading a handful of other posts out there, some of which actually seemed to be plagiarized from each other, I realized that most of what I was reading was also in reference to React Router v4.

Between you and me, I don’t really know what the main differences are between v4 and v5 of React router. What I do know, is that I wasn’t totally satisfied with anything I had read so I had set out to solve the problem my way.

The big things I had found to be missing were in regard to handling routes that should only be available when a user is unauthenticated and nobody seems to have documented the process while using TypeScript or a proper style guide.

I’m a huge fan of TypeScript and I don’t write much code without the safety net of the Airbnb style guide, so most of the code I was slinging around needed massaged quite a bit to make it work for me.

That said, to handle juggling both routes that require authentication, and those that require the user to be unauthenticated, I added two new components that act as wrappers for React Router’s Route component.

The following code will assume that you have some sort of helper method to tell you if the user is properly authenticated. For me, that’s just an isAuthenticated method that checks to see if we have a JSON web token available.

Also, note the limited nature of which route properties I’m passing back to the Route component. I’m not sure if that will be enough long term, but for the sake of not triggering eslint, specifically the react/jsx-props-no-spreading rule, I’m limiting the scope of what I’m passing through.

AuthenticatedRoute.component.tsx

import React from 'react';
import { Redirect, Route, RouteProps } from 'react-router-dom';

import { isAuthenticated } from '../helpers';

const RouteAuthenticated = ({ component: Component, path }: RouteProps) => {
  if (!isAuthenticated()) {
    return <Redirect to="/login" />;
  }

  return <Route component={Component} path={path} />;
};

export default RouteAuthenticated;

UnauthenticatedRoute.component.tsx

import React from 'react';
import { Redirect, Route, RouteProps } from 'react-router-dom';

import { isAuthenticated } from '../helpers';

const RouteUnauthenticated = ({ component: Component, path }: RouteProps) => {
  if (isAuthenticated()) {
    return <Redirect to="/dashboard" />;
  }

  return <Route component={Component} path={path} />;
};

export default RouteUnauthenticated;

App.component.tsx

import React from 'react';
import { Router, Redirect, Switch } from 'react-router-dom';

import { history, isAuthenticated } from '../helpers';

import Dashboard from './Dashboard.component';
import LoginForm from './LoginForm.component';
import RouteAuthenticated from './RouteAuthenticated.component';
import RouteUnauthenticated from './RouteUnauthenticated.component';
import SignupForm from './SignupForm.component';

const fallbackUri = `${isAuthenticated() ? '/dashboard' : '/login'}`;

const App = () => (
  <div className="container">
    <Router history={history}>
      <Switch>
        <RouteUnauthenticated path="/signup" component={SignupForm} />
        <RouteUnauthenticated path="/login" component={LoginForm} />

        <RouteAuthenticated path="/dashboard" component={Dashboard} />

        <Redirect to={fallbackUri} />
      </Switch>
    </Router>
  </div>
);

export default App;

With the addition of the two new components wrapping the Route component, I’m able to route accordingly, so folks that are already authenticated can’t get to the login or sign up forms.

You certainly could combine the RouteAuthenticated and RouteUnauthenticated components into a single component that accepts a new property to tell you if it should require authentication or not. If you’re using TypeScript this will probably result in needing to extend the ReactProps since your new property won’t exist on that type / interface.

What about routes that should be available to both authenticated users and users that have yet to authenticate? Just utilize the Route component from React Router and you should be good to go!

Join the Conversation

Good stuff? Want more?

Weekly emails about technology, development, and sometimes sauerkraut.

100% Fresh, Grade A Content, Never Spam.

About Josh

Husband. Father. Pug dad. Musician. Founder of Holiday API, Head of Engineering and Emoji Specialist at Mailshake, and author of the best damn Lorem Ipsum Library for PHP.

Currently Reading

Parasie Eve

Previous Reads

Buy Me a Coffee Become a Sponsor

Related Articles