ReactJS protected routes

Creating apps, products, or any sort of website in ReactJs is a mostly smooth sail. It is an excellent library for many occasions. Of course, like any other piece of JavaScript, it has its positives and negatives.

When it comes to security and protection, it is often a hassle and a complication to do in ReactJs. Therefore developers can be frequently seen adding libraries to beat those challenges.

In this small blog post, I will show you a super neat way of resolving a piece of those challenges. The public and private routes within the ReactJs application in TypeScript!. Let us begin.

The explanation

The explanation is going to be from the bottom up. This means we are going to start with the lowest level components moving upward.
Things we need are the following:

  1. PrivateRoute.tsx component
  2. Routing/redirect components
  3. Login.tsx component
  4. Home.tsx component (a protected component)

By using these components, we will build a good foundation for the protected and public parts of the app. It will be made so it supports updates and a good amount of routes.

The code

PrivateRoute.tsx

This is a crucial component of private routes. The bread and butter of this blog post. It is used to prevent any non-authenticated users from accessing it. Additionally, it can have logic for special cases, just like in this example.

import React, { FunctionComponent } from "react";  
import { Redirect, Route } from "react-router-dom";  
import { PrivateRouteProps } from "src/root/routing/interfaces/PrivateRouteProps";  
import jwt_decode from "jwt-decode";
import { Redirect, Route, useHistory, useLocation } from "react-router-dom";

export const PrivateRoute: FunctionComponent<PrivateRouteProps> = ({  
  component: Component,  
  ...rest  
}) => {  
  const history = useHistory();
  // You can check special cases you need from the token. And then act correspondingly
  // E.g. If user is admin and the "user" part of the app is prevented for admin.
  // Here the data should be read from your token/cookies.
  // Prior to this you should have code to check whether the token is valid or invalid.
  const accessToken = localStorage.getItem("token");
  const decodedToken = jwt_decode(accessToken);

   if (decodedToken.userData.isAdmin) {
    history.push("/admin-panel");
   }

  return (  
    <Route  
      {...rest}  
      render={(props) => {  
        // logic for authenticated user to access /app part goes here.
        // e.g. check if user is logged-in logic.
        const isLoggedIn = true;  

        return isLoggedIn ? (  
          <Component {...props} />  
        ) : (  
          <Redirect to={"/login"} />  
        );  
   }}  
   />  
  );  
};

Routing components

We have AppRouting and NonAuthRouting here. AppRouting will be used when a user is authenticated. In other words protected routes. The NonAuthRouting will be used for routes that have components used to authenticate a user.

import { FunctionComponent} from "react";
import { SomeComponent } from "...";

export const AppRouting: FunctionComponent = () => {
  <Switch>
    // add your routes & additional routings which need authenticated user here.
    <Route path={"/some-auth-route"} component={SomeComponent} />
  </Switch>
}
};
import { FunctionComponent} from "react";
import { LoginComponent } from "...";

export const AuthRouting: FunctionComponent = () => {
  <Switch>
    // add your routes & additional routings to authenticate user here.
    <Route path={"/non-auth/login"} component={LoginComponent} />
  </Switch>
}
};

The Route view

This component is a wrapper for the two ones above. It will contain the main route passages for AppRouting and the NonAuthRouting. This component should be one of the uppermost components in your app from a level perspective. Near App.tsx or Index.tsx

import { FunctionComponent } from "react";  
import { Redirect, Route } from "react-router-dom";  
import { AppRouting } from "src/root/routing/AppRouting";  
import { NonAuthRouting } from "src/root/routing/AuthRouting";  
import { PrivateRoute } from "src/root/routing/PrivateRoute";  

export const RouteView: FunctionComponent = () => {  
  // redirects from "/" to "/home"
  const renderRootRedirect = () => <Redirect to={"/home"} />;  
  return (  
    <>  
      <Route exact={true} path="/" render={renderRootRedirect} />  
      <PrivateRoute path={"/app"} component={AppRouting} />  
      <Route path={"/non-auth"} component={NonAuthRouting} />  
   </>  );  
};

Conclusion

Public and private routes can be problematic to implement. A lot of developers search for tons of routing options and different implementations. Personally, I have found this approach works the best. Hopefully, it will help anyone who stumbles upon this!