Depending on your abilities or needs, protecting routes in React JS may be difficult or simple, but in this article, we’ll examine how to protect routes without difficulty.
In the previous article, we created and configured routing in React. In this article, we will expand on that project. (I strongly encourage that you read that article if you haven’t already)
How to setup routing in ReactJS – Adnan Halilovic Blog
To protect a route, I will create a new component called ProtectedRoute and wrap any component that requires protection with it. The protected route component is responsible for performing authorization and authentication checks. In the expectedRoles property, we will simply list the roles that have access to the component. Thus, the code should seem as follows:
import { Navigate } from 'react-router';
import { userRoles } from './constants';
const ProtectedRoute = ({ expectedRoles, children }) => {
/*
The following constants are just `hardcoded` values that you would probably
get from JWT or your server, or your own logic to check wether a user
is logged in or not, and to get the user's roles.
*/
const isAuthorized = true;
const areRolesRequired = !!expectedRoles?.length;
const roles = [userRoles.admin];
// Checking if expected and user roles match
const rolesMatch = areRolesRequired ? expectedRoles.some((r) => roles.indexOf(r) >= 0) : true;
/*
If the user is not logged in or authorized, we are just redirecting to home
(you could add any other redirection, such as unauthorized page or anything else).
*/
if (!isAuthorized || !rolesMatch) {
return <Navigate to="/" replace />;
}
// If the user passes all check we are returning the child component (our actual component for a route).
return children;
};
export default ProtectedRoute;
The next step is to configure our routes-config.js
file with the route configuration that we used in the previous article.
import About from '../../features/about/about';
import Contact from '../../features/contact/contact';
import Home from '../../features/home/home';
import NotFound from '../../features/not-found/not-found';
import { userRoles } from './constants';
import ProtectedRoute from './protected-route';
import appRoutes from './routes';
const routesConfig = [
{
path: appRoutes.HOME,
element: <Home />,
},
{
path: appRoutes.ABOUT,
element: (
<ProtectedRoute expectedRoles={[userRoles.editor]}>
<About />
</ProtectedRoute>
),
children: [
{
path: appRoutes.ABOUT_HOME,
element: <Home />,
},
],
},
{
path: appRoutes.CONTACT,
element: <Contact />,
},
{
path: appRoutes.UNKNOWN,
element: <NotFound />,
},
];
export default routesConfig;
In addition, you may have seen the constants file containing the names of our user roles. (This is optional, but I prefer to have all the strings in separate constant files because it is better structured and you will likely make fewer mistakes and typos with this method.)
export const userRoles = {
editor: 'Editor',
admin: 'Admin',
guest: 'Guest',
};
Conclusion
As you’ve seen in the post, we’ve discussed one of the options/methods for securing routes in react-router v6. This technique appeals to me because I like object-based routes and the suggested way works well with them.
You can download the repo with the project from my GitHub page: adnan-halilovic/protecting-routes-in-react (github.com)