AI Prompt to Build a React Authentication Flow With Protected Routes
This AI prompt for a React authentication flow generates the pieces a typical app needs to gate pages behind a login: a login form, an auth context that tracks the current user and token, and a protected-route wrapper that redirects unauthenticated users. It's aimed at frontend developers wiring up auth against an existing backend API who want a consistent starting structure instead of assembling it from scattered tutorial snippets.
The prompt separates concerns the way a real app should: a context provider owns the session state and exposes login/logout functions, while a ProtectedRoute component checks that state and redirects to a login page when there's no valid session, rather than letting protected pages render first and flash their content. It also asks the model to handle the loading state while the session is being verified on page load, which is the detail most quick auth examples skip and that causes a visible flash of protected content before redirect.
Because this kind of prompt produces several interrelated files at once, running the filled-in version through Prompt Optimizer in its Coding mode first can tighten the constraints around state shape and error handling before you spend a generation cycle on it.
Prompt template
Want it sharper? Optimize this prompt with Prompt Optimizer, check it with the Prompt Debugger or shorten it with the Token Optimizer.
Example input
Example output
// AuthContext.jsximport { createContext, useContext, useEffect, useState } from 'react';const AuthContext = createContext(null);export function AuthProvider({ children }) {const [user, setUser] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {fetch('/api/session', { credentials: 'include' }).then((res) => (res.ok ? res.json() : null)).then((data) => setUser(data)).finally(() => setLoading(false));}, []);async function login(email, password) {const res = await fetch('/api/login', {method: 'POST',credentials: 'include',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ email, password }),});if (!res.ok) throw new Error('Invalid credentials');const data = await res.json();setUser(data);}async function logout() {await fetch('/api/logout', { method: 'POST', credentials: 'include' });setUser(null);}return (<AuthContext.Provider value={{ user, loading, login, logout }}>{children}</AuthContext.Provider>);}export const useAuth = () => useContext(AuthContext);
// ProtectedRoute.jsximport { Navigate, useLocation } from 'react-router-dom';import { useAuth } from './AuthContext';export function ProtectedRoute({ children }) {const { user, loading } = useAuth();const location = useLocation();if (loading) return <div>Checking session...</div>;if (!user) return <Navigate to="/login" state={{ from: location }} replace />;return children;}
The AuthProvider wraps the app and checks for an existing session cookie on load, exposing user/loading state and login/logout to the rest of the tree. ProtectedRoute reads that context, shows a loading indicator during the initial check, and redirects to /login while remembering the page the user was trying to reach.
When to use it
- You're starting a new React app that needs login-gated pages and want a clean auth context structure from the start.
- You're replacing ad-hoc
if (user)checks scattered across pages with a single reusable protected-route pattern. - You need to handle the brief loading state while a stored token is being verified on app startup.
- You want a consistent pattern for redirecting unauthenticated users back to login and then on to their original destination after they sign in.
Best practices
- Specify exactly how the token is stored (memory, httpOnly cookie, or localStorage) since this changes the security model and the code the model should generate.
- Ask for a loading state in the auth context so protected routes don't render briefly before a redirect fires on page refresh.
- Request that the protected-route component preserve the originally requested URL so the user lands there after logging in, not just on a generic home page.
- Specify your routing library version (React Router v6 vs. v7, for example) since the redirect and route-nesting APIs differ between versions.
Common mistakes
- Not specifying token storage, which often results in plain localStorage being used by default even when that's not appropriate for the app's security needs.
- Skipping the loading state, causing protected content to flash on screen before the redirect to login happens.
- Forgetting to ask for logout behavior that clears both the stored token and any cached user data in the context.
- Not stating the routing library and version, leading to generated code that mixes APIs from different React Router versions.
FAQs
How do I keep a user logged in after a page refresh?
Store the session server-side behind an httpOnly cookie, then call a session-check endpoint on initial app load to restore the user into your auth context before rendering protected routes.
Should I store the auth token in localStorage or a cookie?
An httpOnly cookie set by the server is generally safer since it isn't readable by JavaScript, which reduces exposure to XSS; localStorage is simpler but should be avoided for sensitive tokens if you can use cookies instead.
How do I redirect a user back to the page they wanted after login?
Have the protected route pass the current location in the redirect state when sending the user to the login page, then read that state after a successful login to navigate them to their original destination.
Does this pattern work with Next.js instead of React Router?
The same separation (auth context, loading state, redirect on missing session) applies, but Next.js typically handles the redirect in middleware or a layout component instead of a client-side ProtectedRoute wrapper.
What tool pairs well with this prompt?
Prompt Optimizer — its Coding mode is useful for tightening the constraints around state shape and error handling in a multi-file prompt like this before you spend a generation cycle on it.