Frontend30-45 min
React Protected Routes Implementation
This guide builds an auth context, protected route wrapper, public-only route wrapper, redirect behavior, and verification checklist for frontend route access.
ReactReact RouterTypeScript
Prerequisites
- A React app using React Router.
- A login flow that can store the current user or access token.
- A `/login` route and at least one private route.
1
Create an auth provider
Implementation snippet
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
api.getMe()
.then((data) => setUser(data.user))
.catch(() => setUser(null))
.finally(() => setLoading(false));
}, []);
return <AuthContext.Provider value={{ user, setUser, loading }}>{children}</AuthContext.Provider>;
}
export function useAuth() {
return useContext(AuthContext);
}2
Create a protected route
- Show a loading state while the app checks the current session.
- Redirect anonymous users to login.
- Preserve the page they attempted to open.
- Render child routes when the user is authenticated.
Implementation snippet
import { Navigate, Outlet, useLocation } from "react-router-dom";
export function ProtectedRoute() {
const { user, loading } = useAuth();
const location = useLocation();
if (loading) return <div>Loading...</div>;
if (!user) return <Navigate to="/login" state={{ from: location }} replace />;
return <Outlet />;
}3
Wrap private routes
Implementation snippet
<Routes>
<Route path="/login" element={<Login />} />
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Route>
</Routes>4
Redirect after login
Implementation snippet
function Login() {
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || "/dashboard";
async function handleLogin(values) {
await login(values);
navigate(from, { replace: true });
}
}5
Redirect authenticated users away from login
Implementation snippet
export function PublicOnlyRoute() {
const { user, loading } = useAuth();
if (loading) return <div>Loading...</div>;
if (user) return <Navigate to="/dashboard" replace />;
return <Outlet />;
}6
Verification checklist
Checklist
- Anonymous users opening `/dashboard` are redirected to `/login`.
- After login, users return to the originally requested page.
- Authenticated users do not see the login page.
- A loading state appears while the session is being checked.
- Backend API routes are still protected separately.