Introduction
In React applications, not all routes should be accessible to every user. For instance, admin pages should be restricted, and certain content may only be visible to logged-in users. Route guards and redirects help manage access control and navigation flow.
React Router provides tools like the Navigate component and conditional rendering to implement route protection and handle redirections based on authentication or user roles.
In this post, we will cover:
- Implementing route guards using the
Navigatecomponent - Conditional rendering of routes based on user roles
- Example: Admin routes vs regular user routes
- Handling redirection after login
Understanding Route Guards
Route guards are checks performed before rendering a route to determine whether the user has permission to access it.
- If access is allowed, the route renders normally.
- If access is denied, the user is redirected to a different page (e.g., login page or home page).
Key Use Cases
- Protecting admin pages from regular users.
- Redirecting unauthenticated users to the login page.
- Showing different routes for different user roles.
Using Navigate for Redirection
React Router’s Navigate component is used to redirect users programmatically.
Syntax
import { Navigate } from 'react-router-dom';
<Navigate to="/login" replace />
to: The target path for redirection.replace: Replaces the current entry in the history stack (prevents back navigation).
Implementing a Simple Route Guard
A basic route guard checks whether the user is authenticated.
import React from 'react';
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ isAuthenticated, children }) {
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return children;
}
export default ProtectedRoute;
Usage
<Routes>
<Route
path="/dashboard"
element={
<ProtectedRoute isAuthenticated={user.isLoggedIn}>
<Dashboard />
</ProtectedRoute>
}
/>
</Routes>
- Only logged-in users can access
/dashboard. - Unauthenticated users are redirected to
/login.
Conditional Rendering Based on User Roles
Applications often have multiple user roles, such as admin and regular users. Routes can be conditionally rendered based on the user’s role.
Example: Role-Based Guard
function RoleBasedRoute({ user, allowedRoles, children }) {
if (!user.isLoggedIn) {
return <Navigate to="/login" replace />;
}
if (!allowedRoles.includes(user.role)) {
return <Navigate to="/unauthorized" replace />;
}
return children;
}
Usage
<Routes>
<Route
path="/admin"
element={
<RoleBasedRoute user={currentUser} allowedRoles={['admin']}>
<AdminDashboard />
</RoleBasedRoute>
}
/>
<Route
path="/profile"
element={
<RoleBasedRoute user={currentUser} allowedRoles={['admin', 'user']}>
<Profile />
</RoleBasedRoute>
}
/>
</Routes>
/adminis accessible only to users with the'admin'role./profileis accessible to both admin and regular users.- Unauthorized users are redirected to
/unauthorized.
Handling Redirection After Login
Redirecting users to the originally requested page after login improves user experience.
Step 1: Capture the Target URL
import { useLocation, Navigate } from 'react-router-dom';
function ProtectedRoute({ isAuthenticated, children }) {
const location = useLocation();
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
useLocation()gets the current location object.state.fromstores the intended destination before redirection.
Step 2: Redirect After Login
import { useLocation, useNavigate } from 'react-router-dom';
function Login({ login }) {
const location = useLocation();
const navigate = useNavigate();
const from = location.state?.from?.pathname || '/';
const handleLogin = () => {
login(); // Perform login logic
navigate(from, { replace: true }); // Redirect to original page
};
return <button onClick={handleLogin}>Login</button>;
}
- After successful login, the user is redirected to the page they initially tried to access.
- Defaults to
/if nofromis specified.
Example: Admin vs Regular User Routes
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
function AdminDashboard() { return <h1>Admin Dashboard</h1>; }
function UserDashboard() { return <h1>User Dashboard</h1>; }
function Login() { return <h1>Login Page</h1>; }
function Unauthorized() { return <h1>Unauthorized Access</h1>; }
const currentUser = { isLoggedIn: true, role: 'user' }; // Example user
function RoleBasedRoute({ user, allowedRoles, children }) {
if (!user.isLoggedIn) return <Navigate to="/login" replace />;
if (!allowedRoles.includes(user.role)) return <Navigate to="/unauthorized" replace />;
return children;
}
function App() {
return (
<Router>
<Routes>
<Route
path="/admin"
element={
<RoleBasedRoute user={currentUser} allowedRoles={['admin']}>
<AdminDashboard />
</RoleBasedRoute>
}
/>
<Route
path="/dashboard"
element={
<RoleBasedRoute user={currentUser} allowedRoles={['user', 'admin']}>
<UserDashboard />
</RoleBasedRoute>
}
/>
<Route path="/login" element={<Login />} />
<Route path="/unauthorized" element={<Unauthorized />} />
</Routes>
</Router>
);
}
export default App;
Key Points:
- Admin routes are protected using role-based checks.
- Regular users cannot access admin pages.
- Unauthorized access redirects to a dedicated page.
Best Practices
- Centralize route guards: Avoid duplicating logic by creating reusable components.
- Use
Navigatefor redirection instead of manualwindow.locationchanges. - Store the original location to redirect users after login.
- Protect sensitive routes on both frontend and backend.
- Provide meaningful feedback for unauthorized access (e.g., a 403 page).
- Combine role and authentication checks for flexible access control.
- Keep guards simple: Avoid adding business logic inside guard components.
Leave a Reply