React Router: Build Protected Routes and Authentication-Based Navigation
Learn how to use React Router to create multiple pages, implement protected routes, redirect unauthenticated users, preserve intended destinations, and organize navigation in React applications.
Prerequisites: Basic React components, props, state, and JavaScript
React Router: Build Protected Routes and Authentication-Based Navigation
A React application often starts with a single page, but real projects quickly need more. A job portal might have a jobs page, job details, a login screen, and a private dashboard. An e-commerce application might have a product catalog, cart, account page, and order history.
React Router helps developers navigate between these screens without building a separate React application for every URL.
However, navigation becomes more complicated when some pages should be available only to authenticated users. This is where protected routes become useful.
In this tutorial, you will build a small routing structure, create a reusable protected-route component, redirect users to login, and understand why frontend route protection is not a replacement for backend authorization.
1. What Does React Router Do?
React Router connects URL paths to React components. When a user navigates to a route, the application renders the corresponding page while maintaining the usual single-page application experience.
For example:
URL
Page
/
Home
/jobs
Job listings
/login
Login
/dashboard
Private dashboard
/profile
User profile
Instead of manually checking window.location.pathname throughout your application, you can define routes in one place.
2. Install React Router
Create a React project using Vite if you do not already have one:
bash
npm create vite@latest react-routing-demo -- --template react
cd react-routing-demo
npm install
npm install react-router
npm run dev
This tutorial uses the current React Router declarative API with imports from react-router. If your existing project uses React Router v6, you may see imports from react-router-dom; follow the API and installation instructions for the version installed in your project.
3. Create Your First Routes
Create a simple routing structure using BrowserRouter, Routes, and Route.
Route associates a path with an element to render.
For example, navigating to /jobs renders the Jobs component.
4. Navigate Without Reloading the Entire Page
React Router provides Link for navigation between application routes.
jsx
import { Link } from "react-router";
export default function Navigation() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/jobs">Jobs</Link>
<Link to="/login">Login</Link>
</nav>
);
}
Use Link for ordinary navigation rather than attaching custom click handlers to every link.
For navigation triggered by an event, such as after a successful login, React Router also provides useNavigate.
5. Understand Protected Routes
A protected route is a route whose content should be displayed only when certain conditions are satisfied.
For example, a job portal might allow anyone to browse jobs but require authentication to access a personal dashboard.
Diagram / Architecture
The frontend can use authentication state to decide which page to display. The server must still enforce access rules when protected data or operations are requested.
6. Build a Reusable Protected Route
Rather than repeating authentication checks on every page, create a reusable component.
This component checks whether the user is authenticated.
If authentication is missing, it redirects the user to the login page and saves the attempted location in navigation state.
If authentication is present, Outlet renders the matching child route.
The isAuthenticated prop in this example is supplied by the application's authentication state. It should not be based on a freely editable value that users can change to grant themselves access.
7. Add a Protected Dashboard Route
You can now organize your application so the dashboard is nested inside the protected route.
jsx
import { BrowserRouter, Routes, Route } from "react-router";
import { ProtectedRoute } from "./ProtectedRoute";
function Home() {
return <h1>Welcome to the Job Portal</h1>;
}
function Login() {
return <h1>Sign in to your account</h1>;
}
function Dashboard() {
return <h1>Your Dashboard</h1>;
}
function Jobs() {
return <h1>Browse Jobs</h1>;
}
export default function App() {
const isAuthenticated = false;
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/jobs" element={<Jobs />} />
<Route path="/login" element={<Login />} />
<Route element={
<ProtectedRoute isAuthenticated={isAuthenticated} />
}>
<Route path="/dashboard" element={<Dashboard />} />
</Route>
</Routes>
</BrowserRouter>
);
}
In this demonstration, isAuthenticated is set to false, so the dashboard redirects to the login route. The value is deliberately hardcoded to illustrate the routing behavior; a real application should obtain authentication status from its authentication provider or session state.
8. Redirect Users Back to Their Original Page
Imagine someone opens /dashboard without signing in. After login, it is often better to return that person to the dashboard instead of always sending them to the home page.
React Router can preserve the intended location in navigation state.
A simplified login component looks like this:
jsx
import { useLocation, useNavigate } from "react-router";
export default function Login({ onLogin }) {
const location = useLocation();
const navigate = useNavigate();
async function handleLogin(event) {
event.preventDefault();
// onLogin should authenticate with the real backend.
await onLogin();
const destination = location.state?.from;
const target = destination
? `${destination.pathname}${destination.search}${destination.hash}`
: "/dashboard";
navigate(target, { replace: true });
}
return (
<form onSubmit={handleLogin}>
<h1>Login</h1>
<button type="submit">Sign in</button>
</form>
);
}
This is a routing example, not a complete authentication implementation. The onLogin function must actually authenticate the user and reject unsuccessful attempts. Production code should also handle loading, errors, and safe redirect destinations.
9. Handle Unknown Routes
Users can enter an invalid URL manually or follow an outdated link. A fallback route helps display a useful not-found page.
jsx
import { Routes, Route } from "react-router";
function NotFound() {
return <h1>404 - Page Not Found</h1>;
}
function AppRoutes() {
return (
<Routes>
<Route path="/" element={<h1>Home</h1>} />
<Route path="/jobs" element={<h1>Jobs</h1>} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
A fallback route improves navigation and prevents unknown paths from displaying an empty page.
Remember that production hosting must also be configured to serve the single-page application's entry point for valid client-side routes.
10. Common React Router Mistakes
Using Ordinary Anchors for Every Internal Link
A standard <a href="/jobs"> can trigger a full document navigation. Use React Router's Link for ordinary internal navigation when appropriate.
Forgetting to Render Nested Routes
When using nested routes, the parent layout or guard generally needs an Outlet to display the matching child route.
Redirecting Without Preserving the Destination
Always sending users to the same page after login can create a frustrating experience. Preserve the intended destination when appropriate and validate redirect targets safely.
Treating Frontend Protection as Security
A protected route only controls what the frontend renders. It does not stop someone from calling an API directly. The backend must verify authentication and authorization for every protected operation.
Storing Sensitive Authentication Data Insecurely
Avoid treating a client-controlled flag or arbitrary local-storage value as proof of identity. Use a properly designed authentication system, secure session handling, and appropriate server-side checks.
Forgetting Production Hosting Configuration
Client-side routing can work locally but fail after deployment if the hosting platform returns a 404 for nested URLs. Configure the host's fallback behavior according to the deployment environment.
11. When Should You Use Nested Routes?
Nested routes are especially useful when several pages share a common layout.
Scenario
Example
Dashboard
/dashboard/profile
Job portal
/jobs/:jobId
E-commerce
/account/orders
Admin panel
/admin/users
A shared parent layout can contain navigation, a sidebar, or a protected-route check while an Outlet renders the active child page.
12. Practical React Router Checklist
Before deploying a routed application, check that:
Protected pages check the real authentication state.
Login redirects work as intended.
Nested routes render through an Outlet where required.
Unknown routes display a not-found page.
API endpoints independently enforce authorization.
Failed login attempts are handled correctly.
Direct navigation to nested URLs works after deployment.
The application uses the API supported by its installed React Router version.
Final Takeaway
React Router provides the navigation structure needed to build multi-page experiences inside a React application. With nested routes, reusable layouts, navigation state, and protected-route components, developers can create more organized applications without duplicating route logic.
Start with a few basic routes, introduce nested layouts as the application grows, and add authentication-aware navigation when necessary.
Most importantly, remember that frontend route protection controls the interface, while backend authorization protects the actual data and operations. Both are necessary for a secure application.
Learn how to organize a JavaScript project with a clean folder structure, reusable modules, separated responsibilities, consistent naming, and practical patterns that make projects easier to maintain and scale
Learn how the Next.js App Router works with pages, layouts, dynamic routes, Server and Client Components, data fetching, loading states, metadata, and practical application structure.
Comments (0)
Be the first to share your thoughts.