What is React Router?
React Router is the standard routing library for React. As the documentation states:
React Router is a fully-featured client and server-side routing library for React, a JavaScript library for building user interfaces.
It enables navigation between different components in a React application, allows changing the browser URL, and keeps the UI in sync with the URL.
What are Protected Routes?
Protected Routes (also known as Private Routes) are routes that require user authorization to access. Typically, this means a user must be authenticated to view certain pages.
Common use cases include:
- User dashboards
- Admin panels
- User profile pages
- Any content that requires login
Tutorial Setup
Before we begin, make sure you have a React application set up. You can use either Vite or Create React App.
Using a Bundler (Vite or Create React App)
Install React Router:
Using Yarn
yarn add react-router-dom@6
Using NPM
npm install react-router-dom@6
Implementation Steps
1. Connect URL in main.jsx
First, wrap your app with BrowserRouter
to enable routing:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import "./index.css";
import AppRouter from "./AppRouter";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<AppRouter />
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
2. Create AppRouter.jsx
Set up your routes structure:
import React from "react";
import { Routes, Route } from "react-router-dom";
// Layout
import Layout from "./Layout";
// Pages
import PublicPage from "./pages/Public";
import LoginPage from "./pages/Login";
import PrivatePage from "./pages/Private";
const AppRouter = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<PublicPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/private" element={<PrivatePage />} />
</Route>
</Routes>
);
};
export default AppRouter;
3. Create Layout Component
Create a layout with navigation and authentication provider:
import React from "react";
import { Link, Outlet } from "react-router-dom";
import { AuthProvider } from "./AuthProvider";
const Layout = () => {
return (
<AuthProvider>
<header>
<nav>
<Link to="/">Public Page</Link>
<Link to="/private">Private Page</Link>
</nav>
</header>
<main>
<Outlet />
</main>
</AuthProvider>
);
};
export default Layout;
4. Create Authentication Provider
Implement authentication context for managing user state:
import React, { createContext, useState, useContext } from "react";
import { useNavigate, useLocation } from "react-router-dom";
const AuthContext = createContext(null);
export const AuthProvider = ({ children }) => {
const navigate = useNavigate();
const location = useLocation();
const [user, setUser] = useState(null);
const handleLogin = async () => {
const user = { email: "[email protected]" };
setUser(user);
const origin = location.state?.from?.pathname || "/private";
navigate(origin);
};
const handleLogout = () => {
setUser(null);
};
const value = {
user,
onLogin: handleLogin,
onLogout: handleLogout,
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
return useContext(AuthContext);
};
5. Create Page Components
Public Page
import React from "react";
const PublicPage = () => {
return (
<div>
<h1>Public Page</h1>
<p>This page is accessible to everyone.</p>
</div>
);
};
export default PublicPage;
Login Page
import React from "react";
import { useAuth } from "../AuthProvider";
const LoginPage = () => {
const { onLogin } = useAuth();
return (
<div>
<h1>Login Page</h1>
<button onClick={onLogin}>Sign In</button>
</div>
);
};
export default LoginPage;
Private Page
import React from "react";
import { useAuth } from "../AuthProvider";
const PrivatePage = () => {
const { user, onLogout } = useAuth();
return (
<div>
<h1>Private Page</h1>
<p>Welcome, {user?.email}!</p>
<button onClick={onLogout}>Sign Out</button>
</div>
);
};
export default PrivatePage;
6. Create RequireAuth Component
This is the key component that protects routes:
import React from "react";
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "./AuthProvider";
const RequireAuth = ({ children }) => {
const { user } = useAuth();
const location = useLocation();
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
};
export default RequireAuth;
7. Update AppRouter with Protected Routes
Wrap private routes with RequireAuth
:
import React from "react";
import { Routes, Route } from "react-router-dom";
// Layout
import Layout from "./Layout";
// Pages
import PublicPage from "./pages/Public";
import LoginPage from "./pages/Login";
import PrivatePage from "./pages/Private";
// Auth
import RequireAuth from "./RequireAuth";
const AppRouter = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<PublicPage />} />
<Route path="/login" element={<LoginPage />} />
<Route
path="/private"
element={
<RequireAuth>
<PrivatePage />
</RequireAuth>
}
/>
</Route>
</Routes>
);
};
export default AppRouter;
How It Works
- AuthProvider: Manages authentication state and provides login/logout functions
- RequireAuth: Checks if user is authenticated before rendering protected content
- Navigation: Redirects unauthenticated users to login page
- State Preservation: Remembers where users were trying to go before login
Best Practices
- Centralize Authentication: Use Context API or state management libraries
- Secure API Calls: Always validate authentication on the backend
- Handle Loading States: Show loading indicators during auth checks
- Persist Auth State: Use localStorage or cookies for session persistence
- Clear Navigation: Provide clear login/logout options
Conclusion
Protected routes are essential for building secure React applications. React Router v6 makes implementation straightforward with its hooks and component-based approach. Remember that client-side protection is just the first layer - always validate authentication on your backend for true security.
This pattern can be extended to handle different user roles, permissions, and more complex authentication flows. The key is understanding the core concepts and adapting them to your specific needs.