Skip to content
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

  1. Show a loading state while the app checks the current session.
  2. Redirect anonymous users to login.
  3. Preserve the page they attempted to open.
  4. 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.

Need implementation help?

Want this built correctly in your codebase?

Send us your stack, repo context, and the feature you need. We will help you implement it cleanly and hand over the working code.

Free scoping callFixed timelineFull source ownership
Get implementation help