Radoslav's Blog

Protected Routes With react-router v4

Posted on January, 31 2019 in react

Protected Routes With react-router v4
React Router Auth

In this post I will show you how you can protect some of the pages in your React application, so they can not be accessed by non-authorized users.

The libraries we will use in this example:

  1. react@16.7.0
  2. react-router-dom@4.3.1

To keep it simple I won't include any state management libraries like redux, we will use a simple component state. The idea is the same so you can use redux as well if you wish.

First steps

Lets create new empty react project.

npx create-react-app protected-routes

To verify everything is working lets start the project in the browser:

cd protected-routes
yarn start

You should see the default page.

Nice, that was easy! Next we need to add react-router to our app.

yarn add react-router-dom

After this we will edit `src/App.js` to add some routes and pages.

import React from "react";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import "./App.css";

const Index = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const Users = () => <h2>Users</h2>;

const App = () => (
 <Router>
  <div>
   <nav>
    <ul>
     <li>
      <Link to="/">Home</Link>
     </li>
     <li>
      <Link to="/about/">About</Link>
     </li>
     <li>
      <Link to="/users/">Users</Link>
     </li>
    </ul>
   </nav>

   <div className="content">
     <Switch>
       <Route path="/" exact component={Index} />
       <Route path="/about/" component={About} />
       <Route path="/users/" component={Users} />
     </Switch>
   </div>
 </div>
</Router>
);
  
export default App;

What happened here? We just imported the BrowserRouter, the Route and the Link component. Also we have defined three sample pages we will use for navigation. Now our App component will render a navigation links for those pages. The route component will make sure it renders the correct page component based on the current url we are on.

You should see this now in your browser (please not I've applied some CSS styles to the page to be more pretty, this source code of which you can find at the end of the post):

Nice! If you now click through the menu links you will see each page renders its own content when you visit the link.

Creating PrivateRoute component

In the above example we are defining all our routes with the Route component. For private routes however we will create a new composition component that will check if we are authenticated to view the visited page and if you are not it will redirect you to the home page.

Let's first create our sample auth state. For simplicity we will use the App component, but in real production app those state can come from redux for example.

class App extends React.Component {
 state = {
  isAuthenticated: false,
 };

 render() {
  return (
   <Router>
    // Rest of the code omited for readability
   </Router>
  );
 }
}

After that create `src/PrivateRoute.js`

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

const PrivateRoute = ({ component, isAuthenticated, location, path }) => {
 if (!isAuthenticated) {
  return (
   <Redirect
    to={{
     pathname: "/home",
     state: { redirect: location && location.pathname },
    }}
   />
  );
 }

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

export default PrivateRoute;

Let see what happens here.

  1. We've created a new component that we will use to define our private routes
  2. It accepts the same props as the Route component with the only addition we that we will pass isAuthenticated prop as well.

In the render method of the PrivateRoute we do a simple check to see if the user is allowed to view this page. If not, they will be redirected to the home page. We also pass the current path as redirect query parameter in case we want to redirect them back to the private page once they authenticate.

If they are already authenticated we will render a simple Route component.

So, let's put those things together.

Import `PrivateRoute` in App.js and define `/users/` route as private:

import PrivateRoute from "./PrivateRoute";

.....

<PrivateRoute path="/users/" component={Users} isAuthenticated={this.state.isAuthenticated} />

Now if you try to navigate to `/users` you will be redirected to the home page because you are not authenticated.

Let's add sample authentication so we can test that. Add this method to your app component:

toggleAuth = () => this.setState({
  isAuthenticated: !this.state.isAuthenticated
 });

And add one more item to the header menu:

<li>
  <button className="btn" onClick={this.toggleAuth}>
    {this.state.isAuthenticated ? 'Logout' : 'Login'}
  </button>
</li>

Now we can easily switch between out logged in / logged out state!



You can checkout the source code in Github.


By Radoslav Vitanov

I am a Full Stack developer, experienced with building scalable web applications with Laravel, performant single page applications with React and mobile apps with Flutter.