patterntypescriptModerate
Auth State Persistence and Hydration in React
Viewed 0 times
auth stateloading statesession hydrationflash unauthenticatedreact contextisLoading
Problem
Auth state initialized from an async source (session API, localStorage token) causes a flash of unauthenticated UI or redirects before the session is resolved, leading to jarring UX and incorrect SSR/CSR mismatches.
Solution
Track an isLoading state alongside the auth user. Show a loading skeleton or spinner until the auth check resolves. For SSR frameworks, seed the initial state from the server to avoid the flash entirely.
Why
Auth state is inherently async. Treating isLoading: false as the default causes components to render in an unauthenticated state before the real session arrives, triggering redirects and layout shifts.
Gotchas
- Never conditionally call hooks based on auth state — hooks must run unconditionally
- In Next.js App Router, prefer server-side session reading to avoid client flicker entirely
- Avoid storing sensitive user data (tokens, PII) in React context that is serialized into HTML — it is visible in page source
Code Snippets
Auth context provider with loading state
import { createContext, useContext, useEffect, useState } from 'react';
type AuthState = { user: User | null; isLoading: boolean };
const AuthContext = createContext<AuthState>({ user: null, isLoading: true });
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch('/api/auth/session')
.then((r) => r.json())
.then((data) => setUser(data.user ?? null))
.finally(() => setIsLoading(false));
}, []);
return <AuthContext.Provider value={{ user, isLoading }}>{children}</AuthContext.Provider>;
}
export const useAuth = () => useContext(AuthContext);Revisions (0)
No revisions yet.