How to Secure React Router in React
Securing routes in a React application is essential for protecting sensitive data and ensuring that only authorized users can access specific pages. This article provides a direct, step-by-step guide on how to secure React Router (v6) by implementing a reusable Protected Route component, handling authentication states, and setting up role-based access control.
1. Understanding the Protected Route Pattern
React Router does not have built-in security features. Instead, securing routes relies on a pattern called Protected Routes. This involves creating a wrapper component that checks the user’s authentication status. If the user is authenticated, the component renders the requested page; if not, it redirects them to a login page.
2. Creating a Protected Route Component
Using React Router v6, you can create a ProtectedRoute
component using the <Navigate /> component and the
<Outlet /> layout component.
import { Navigate, Outlet } from 'react-router-dom';
const ProtectedRoute = ({ isAuthenticated }) => {
if (!isAuthenticated) {
// Redirect unauthorized users to the login page
return <Navigate to="/login" replace />;
}
// Render child routes if authenticated
return <Outlet />;
};
export default ProtectedRoute;In this component: * isAuthenticated is a boolean passed
from your application’s authentication state (e.g., Context, Redux, or a
custom hook). * <Navigate to="/login" replace />
redirects the user while replacing the current entry in the history
stack, preventing them from clicking “back” into a secured page. *
<Outlet /> renders the child routes defined inside
this route path.
3. Implementing Protected Routes in Your App Router
Once the ProtectedRoute component is created, wrap your
private routes with it in your main router configuration.
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { useState } from 'react';
import ProtectedRoute from './ProtectedRoute';
import Home from './pages/Home';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import Settings from './pages/Settings';
function App() {
// Replace this with your actual authentication state/context hook
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<Router>
<Routes>
{/* Public Routes */}
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
{/* Secured Routes */}
<Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Route>
</Routes>
</Router>
);
}
export default App;By nesting the /dashboard and /settings
routes inside the ProtectedRoute route, they are
automatically shielded by the authentication check.
4. Implementing Role-Based Access Control (RBAC)
If your application has different user permissions (e.g., Admin vs. User), you can expand the protected route component to check for user roles.
import { Navigate, Outlet } from 'react-router-dom';
const RoleProtectedRoute = ({ isAuthenticated, userRole, allowedRoles }) => {
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (!allowedRoles.includes(userRole)) {
return <Navigate to="/unauthorized" replace />;
}
return <Outlet />;
};
export default RoleProtectedRoute;You can then apply this in your router like this:
<Route
element={
<RoleProtectedRoute
isAuthenticated={isAuthenticated}
userRole={userRole}
allowedRoles={['admin']}
/>
}
>
<Route path="/admin-panel" element={<AdminPanel />} />
</Route>A Crucial Security Reminder
Client-side routing protection is entirely for user experience (UX). It prevents users from seeing UI components they shouldn’t access. However, because client-side JavaScript can be manipulated, true security must always be enforced on your backend server. Ensure all API endpoints requested by your React application validate session tokens or JWTs before returning sensitive data.