Check Your Version
This guide assumes you're using React Router v6. If you're using an older version, refer to the appropriate documentation:
Introduction
When building web applications, you often need to restrict access to certain pages based on authentication status. Protected routes allow you to control which users can access specific parts of your app. For example:
- Public routes (accessible to everyone): Home, Pricing, and Login pages.
- Protected routes (accessible only to authenticated users): Dashboard and Settings pages.
Although React Router doesn’t provide built-in authentication features, it is flexible enough to implement them easily.
⚠️ Note: Any front-end authentication method is only for user experience (UX). Always enforce proper authentication checks on the server-side to prevent unauthorized data access.
Setting Up Authentication
To determine whether a user is authenticated, we'll create a custom authentication hook. For simplicity, this hook will store authentication status in React state.
import * as React from "react";
const authContext = React.createContext();
function useAuth() {
const [authed, setAuthed] = React.useState(false);
return {
authed,
login() {
return new Promise((res) => {
setAuthed(true);
res();
});
},
logout() {
return new Promise((res) => {
setAuthed(false);
res();
});
},
};
}
export function AuthProvider({ children }) {
const auth = useAuth();
return {children} ;
}
export default function AuthConsumer() {
return React.useContext(authContext);
Creating the Basic Routes
We'll set up public and private pages in our app.
import * as React from "react";
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
const Home = () => <h1>Home (Public)</h1>;
const Pricing = () => <h1>Pricing (Public)</h1>;
const Dashboard = () => <h1>Dashboard (Private)</h1>;
const Settings = () => <h1>Settings (Private)</h1>;
const Login = () => <h1>TODO</h1>;
function Nav() {
return (
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/pricing">Pricing</Link></li>
</ul>
</nav>
);
}
export default function App() {
return (
<BrowserRouter>
<Nav />
<Routes>
<Route path="/" element=<Home /> />
<Route path="/pricing" element=<Pricing /> />
<Route path="/dashboard" element=<Dashboard /> />
<Route path="/settings" element=<Settings /> />
<Route path="/login" element=<Login /> />
</Routes>
</BrowserRouter>
);
}
Implementing Login and Logout
We'll modify our Login component to update the authentication status when a user logs in.
import { useNavigate } from "react-router-dom";
import useAuth from "./useAuth";
const Login = () => {
const navigate = useNavigate();
const { login } = useAuth();
const handleLogin = () => {
login().then(() => {
navigate("/dashboard");
});
};
return (
<div>
<h1>Login</h1>
<button onClick={handleLogin}>Log in</button>
</div>
);
};
Protecting Routes with RequireAuth
import { Navigate, useLocation } from "react-router-dom";
import useAuth from "./useAuth";
function RequireAuth({ children }) {
const { authed } = useAuth();
const location = useLocation();
return authed === true
? children
: <Navigate to="/login" replace state={{ path: location.pathname }} />;
}
Redirecting Users After Login
import { useLocation, useNavigate } from "react-router-dom";
import useAuth from "./useAuth";
const Login = () => {
const navigate = useNavigate();
const { login } = useAuth();
const { state } = useLocation();
const handleLogin = () => {
login().then(() => {
navigate(state?.path || "/dashboard");
});
};
return (
<div>
<h1>Login</h1>
<button onClick={handleLogin}>Log in</button>
</div>
);
};