Lesson 54 of 60 – Protected Routes
90%

Protected Routes in React

A protected route is a route that should only be accessible when a user satisfies a required condition, such as being authenticated. In React, protected routes are commonly implemented together with authentication state and React Router.

Note: Frontend protected routes improve navigation and user experience, but they are not a replacement for backend authorization. Sensitive API endpoints must also enforce access rules on the server.

1. What is a Protected Route?

A protected route is a route where access depends on a condition.

For example, a dashboard may only be shown when a user is authenticated.

User
 ↓
Is Authenticated?
 ↙           ↘
Yes           No
 ↓             ↓
Dashboard     Login

2. Why Use Protected Routes?

Protected routes help control which pages users can visit in the frontend application.

Common examples include:

  • Dashboard
  • Profile
  • Admin panel
  • Student panel
  • Account settings
  • Order management

3. Public vs Protected Routes

Public Route Protected Route
Can normally be accessed without login Requires an authentication condition
Example: Home Example: Dashboard
Example: Login Example: Profile

4. React Router and Protected Routes

Protected route logic can be implemented around React Router routes.

<Routes>

    <Route
        path="/login"
        element={<Login />}
    />

    <Route
        path="/dashboard"
        element={<ProtectedRoute>
            <Dashboard />
        </ProtectedRoute>}
    />

</Routes>

5. Authentication State

The application needs some authentication state to determine whether the user is currently authenticated.

const [user, setUser] =
    useState(null);

A non-null user can represent an authenticated application state, depending on how the application is designed.

6. Basic Authentication Check

A simple authentication check can test whether a user exists.

if (user) {

    // User is authenticated

} else {

    // User is not authenticated

}

7. Navigate Unauthenticated Users

React Router's Navigate component can redirect an unauthenticated user to the login page.

import {
    Navigate
} from "react-router-dom";

function ProtectedRoute({
    user,
    children
}) {

    if (!user) {
        return (
            <Navigate
                to="/login"
            />
        );
    }

    return children;
}

8. Basic ProtectedRoute Component

A reusable component can contain the protected route logic.

function ProtectedRoute({
    user,
    children
}) {

    return user
        ? children
        : <Navigate to="/login" />;
}

9. Using ProtectedRoute

<Route
    path="/dashboard"
    element={
        <ProtectedRoute
            user={user}
        >

            <Dashboard />

        </ProtectedRoute>
    }
/>

The Dashboard component is rendered only when the authentication condition allows it.

10. Redirecting to Login

If the user is not authenticated, the protected route can redirect to /login.

return (
    <Navigate
        to="/login"
        replace
    />
);

The replace option can be used when the login redirect should replace the current history entry.

11. Protected Routes with Context

If authentication is stored in Context, a protected route can read the authentication state with useContext().

const {
    user
} = useContext(
    AuthContext
);

12. ProtectedRoute with AuthContext

function ProtectedRoute({
    children
}) {

    const {
        user
    } = useContext(
        AuthContext
    );

    if (!user) {

        return (
            <Navigate
                to="/login"
                replace
            />
        );
    }

    return children;
}

13. Authentication Loading

When the application checks an existing authentication session, the result may not be available immediately.

const {
    user,
    loading
} = useContext(
    AuthContext
);

The route should avoid redirecting before the authentication check has finished.

14. Handling Auth Loading

if (loading) {

    return (
        <p>
            Checking authentication...
        </p>
    );
}

if (!user) {

    return (
        <Navigate
            to="/login"
            replace
        />
    );
}

return children;

15. Protected Dashboard

A dashboard can be placed behind a protected route.

<Route
    path="/dashboard"
    element={
        <ProtectedRoute>
            <Dashboard />
        </ProtectedRoute>
    }
/>

16. Multiple Protected Routes

The same protected route component can protect multiple pages.

<Route
    path="/dashboard"
    element={
        <ProtectedRoute>
            <Dashboard />
        </ProtectedRoute>
    }
/>

<Route
    path="/profile"
    element={
        <ProtectedRoute>
            <Profile />
        </ProtectedRoute>
    }
/>

17. Nested Protected Routes

A protected layout can also contain multiple child routes.

<Route
    element={
        <ProtectedRoute>
            <DashboardLayout />
        </ProtectedRoute>
    }
>

    <Route
        path="/dashboard"
        element={<Dashboard />}
    />

    <Route
        path="/profile"
        element={<Profile />}
    />

</Route>

18. Protected Layout

A protected layout can contain common navigation and an Outlet for child pages.

function ProtectedLayout() {

    return (
        <div>

            <Navbar />

            <Outlet />

        </div>
    );
}

19. Protecting Admin Pages

Authentication and authorization are different. An admin page may require both an authenticated user and an appropriate role.

if (!user) {

    return (
        <Navigate
            to="/login"
        />
    );
}

if (user.role !== "admin") {

    return (
        <Navigate
            to="/dashboard"
        />
    );
}

20. Role-Based Protected Route

function AdminRoute({
    user,
    children
}) {

    if (!user) {

        return (
            <Navigate
                to="/login"
            />
        );
    }

    if (user.role !== "admin") {

        return (
            <Navigate
                to="/dashboard"
            />
        );
    }

    return children;
}

21. Remembering the Requested Page

Sometimes a user visits a protected page before logging in. The application can remember the requested location and return the user after successful authentication.

const location =
    useLocation();

return (
    <Navigate
        to="/login"
        state={{
            from: location
        }}
        replace
    />
);

22. Redirect After Login

The login page can read the previous location and navigate there after successful authentication.

const location =
    useLocation();

const from =
    location.state?.from?.pathname
    || "/dashboard";

navigate(from, {
    replace: true
});

23. Protected Route Flow

User Visits Protected Page
           ↓
    Check Authentication
           ↓
     ┌─────┴─────┐
     ↓           ↓
   Logged       Not Logged
     ↓           ↓
Allow Page      Login
     ↓           ↓
  Dashboard    Login Success
                  ↓
             Protected Page

24. Protected Route with Auth Loading

function ProtectedRoute({
    children
}) {

    const {
        user,
        loading
    } = useContext(
        AuthContext
    );

    if (loading) {

        return (
            <p>
                Checking authentication...
            </p>
        );
    }

    if (!user) {

        return (
            <Navigate
                to="/login"
                replace
            />
        );
    }

    return children;
}

25. Authentication vs Route Protection

Authentication establishes who the user is. Route protection controls the frontend navigation based on authentication state.

Authentication
      ↓
Current User
      ↓
Protected Route
      ↓
Allowed UI

The backend must still verify authentication and authorization for protected API operations.

26. Common Protected Route Mistakes

  • Protecting only the frontend route while leaving the API unprotected.
  • Redirecting before authentication loading has completed.
  • Forgetting to handle logout.
  • Using the wrong authentication state.
  • Protecting a page but not its sensitive API requests.
  • Confusing authentication with authorization.
  • Forgetting the replace option when appropriate.
  • Creating duplicate authentication logic across many components.

27. Protected Route Architecture

AuthProvider
     ↓
Authentication State
     ↓
ProtectedRoute
     ↓
React Router
     ↓
Protected Page
     ↓
Protected API
     ↓
Backend Authorization

28. Complete ProtectedRoute Component

import {
    Navigate,
    useLocation
} from "react-router-dom";

import {
    useContext
} from "react";

function ProtectedRoute({
    children
}) {

    const {
        user,
        loading
    } = useContext(
        AuthContext
    );

    const location =
        useLocation();

    if (loading) {

        return (
            <div>
                Checking authentication...
            </div>
        );
    }

    if (!user) {

        return (
            <Navigate
                to="/login"
                state={{
                    from: location
                }}
                replace
            />
        );
    }

    return children;
}

export default ProtectedRoute;

29. Complete React Router Example

import {
    BrowserRouter,
    Routes,
    Route,
    Navigate
} from "react-router-dom";

function App() {

    return (
        <BrowserRouter>

            <Routes>

                <Route
                    path="/login"
                    element={
                        <Login />
                    }
                />

                <Route
                    path="/"
                    element={
                        <Home />
                    }
                />

                <Route
                    path="/dashboard"
                    element={
                        <ProtectedRoute>

                            <Dashboard />

                        </ProtectedRoute>
                    }
                />

                <Route
                    path="/profile"
                    element={
                        <ProtectedRoute>

                            <Profile />

                        </ProtectedRoute>
                    }
                />

                <Route
                    path="*"
                    element={
                        <Navigate
                            to="/"
                        />
                    }
                />

            </Routes>

        </BrowserRouter>
    );
}

export default App;

30. Protected Route Best Practices

  • Create a reusable protected route component.
  • Keep authentication state in a consistent location.
  • Handle authentication loading before redirecting.
  • Use Navigate for frontend redirects.
  • Use useLocation when the original destination needs to be remembered.
  • Separate authentication from authorization.
  • Protect sensitive API endpoints on the backend.
  • Use role checks for pages that require specific permissions.
  • Do not rely on hiding frontend routes as a security mechanism.
  • Keep route protection logic organized and reusable.
Authentication
      ↓
ProtectedRoute
      ↓
React Router
      ↓
Protected UI
      ↓
Backend Authorization

📌 Key Points

  • A protected route restricts frontend access based on a condition.
  • Authentication state can determine whether a user is logged in.
  • Navigate can redirect unauthenticated users.
  • ProtectedRoute can be created as a reusable component.
  • Authentication loading should be handled before making redirect decisions.
  • Multiple routes can use the same protection component.
  • Nested routes can be protected with a shared layout.
  • Authentication and authorization are different concepts.
  • Role-based route checks can be used for different user permissions.
  • Backend authorization is still required to protect sensitive data and operations.

🧠 Quick Quiz

Question: Which React Router component is commonly used to redirect an unauthenticated user to the login page?