How to Secure Routes in React Router

Securing routes in a React application is essential for protecting sensitive pages and user data from unauthorized access. This article provides a straightforward guide on how to implement protected routes in React Router, covering the creation of a reusable wrapper component, managing authentication state, and redirecting unauthorized users to a login page.

To secure routes in React Router (v6 and above), you need to create a route guard. This is typically a component that checks the user’s authentication status before rendering the requested page. If the user is authenticated, the component grants access; otherwise, it redirects them to a public page, such as a login screen.

Step 1: Create the ProtectedRoute Component

The ProtectedRoute component acts as a gatekeeper. It checks an authentication condition (like a token in local storage or a state variable from a React Context) and decides whether to render the child components or trigger a redirect.

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 the child routes if authenticated
  return <Outlet />;
};

export default ProtectedRoute;

In this component: * Navigate: A React Router component used to programmatically redirect users. The replace prop ensures the unauthorized page is replaced in the browser history, preventing users from clicking “Back” to access the secured route. * Outlet: A placeholder component that renders the child routes matched by the router.

Step 2: Implement the Protected Routes in Your App Router

Next, integrate the ProtectedRoute component into your main router configuration. Wrap the routes you want to protect inside the ProtectedRoute element.

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { useState } from 'react';
import ProtectedRoute from './ProtectedRoute';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import Settings from './pages/Settings';
import Home from './pages/Home';

function App() {
  // Replace this state with your actual authentication logic (e.g., Context API, Redux, or Auth0)
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  return (
    <Router>
      <Routes>
        {/* Public Routes */}
        <Route path="/" element={<Home />} />
        <Route path="/login" element={<Login setIsAuthenticated={setIsAuthenticated} />} />

        {/* Secured Routes */}
        <Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
        </Route>
      </Routes>
    </Router>
  );
}

export default App;

How It Works

By nesting the /dashboard and /settings routes inside the ProtectedRoute route, React Router will check the isAuthenticated prop before loading either page. If a non-logged-in user attempts to navigate directly to /dashboard, the ProtectedRoute intercepts the request and instantly redirects them to /login.