Quick Answer
“Firebase auth redirect loops in Next.js usually occur when routing logic evaluates before the asynchronous onAuthStateChanged listener finishes. To fix this, implement a loading state in your AuthContext and block all router redirects until loading is false.”
Similar Error Patterns
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate.FirebaseError: Firebase: Error (auth/network-request-failed).NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mountedAre You Seeing This?
- Flickering screen when loading the dashboard
- Infinite loop between /login and /dashboard
- Maximum update depth exceeded error in console
- Users are redirected to login even if they were previously logged in
One of the most frustrating experiences for a user is attempting to log in, only to be caught in an infinite loop of redirects between the login page and the dashboard, accompanied by a rapidly flickering UI.
This infinite loop is a classic race condition in React and Next.js applications using Firebase Authentication. It occurs when your application's routing logic evaluates the user's authentication state before Firebase has had time to retrieve the token from the browser's IndexedDB.
TL;DR: How to fix the Firebase Auth Loop
- 11. Isolate the Firebase `onAuthStateChanged` listener inside a dedicated React Context Provider.
- 22. Introduce a `loading` state flag that initializes as `true`.
- 33. Block Next.js or React Router from evaluating redirect logic until `loading` is `false`.
- 44. Ensure `useEffect` dependency arrays monitoring the auth state do not contain continuously updating variables.
Root Causes
Asynchronous Auth Initialization (The Race Condition)
When a React app mounts, Firebase takes a few milliseconds to check local storage/IndexedDB for an existing session. If your Next.js router checks `currentUser` synchronously on mount, it will see `null`, redirecting the user to `/login`. Milliseconds later, Firebase confirms the session, redirecting back to `/dashboard`, creating the loop.
Unstable useEffect Dependencies
If your `useEffect` hook that handles the redirection depends on a user object that changes object reference on every render, it will trigger an infinite re-render loop.
useEffect(() => { if (!user) router.push('/login'); }, [user, router]); // if 'user' reference is unstableServer-Side Rendering (SSR) Mismatches
In Next.js, the server renders the initial HTML without access to the user's IndexedDB. The server serves the 'logged-out' layout. Once the client hydrates, Firebase kicks in and updates to the 'logged-in' layout. Without a loading state, this causes a jarring layout shift and potential redirect loop.
Step-by-Step Fix Guide
Create an AuthContext with a Loading State
Wrap your application in a Context Provider that specifically tracks whether Firebase has completed its initial check.
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false); // Crucial: Stop loading only after Firebase responds
});
return () => unsubscribe();
}, []);
return (
<AuthContext.Provider value={{ user, loading }}>
{children}
</AuthContext.Provider>
);
};Block Routing While Loading
In your protected routes or higher-order components (HOCs), intercept the render if `loading` is true. Do not execute redirect logic.
const ProtectedRoute = ({ children }) => {
const { user, loading } = useAuth();
const router = useRouter();
useEffect(() => {
if (!loading && !user) {
router.push('/login');
}
}, [user, loading, router]);
if (loading) return <LoadingSpinner />; // Prevents the flash and redirect loop
return user ? children : null;
};Stabilize Object References
If you map the Firebase `User` object to a custom object, use `useMemo` to prevent the object reference from changing on every render, which would re-trigger `useEffect`.
- Avoid deep object comparisons in `useEffect` arrays.
- Rely on simple primitives if possible: `user?.uid` instead of the whole `user` object in the dependency array.
Caught in an Auth Loop?
Are your users stuck flickering between screens? Let me review your React/Next.js architecture and implement a production-grade auth state pattern.
Get Architecture RescueRelated Errors
Error: Maximum update depth exceeded
This React error accompanies the loop. Verify your `useEffect` dependencies are not causing state updates that trigger the effect again.
NextRouter was not mounted
Occurs when redirecting before the Next.js router is fully ready. Ensure router usage is wrapped in `useEffect`.
Prevention Strategy
- Always assume Firebase authentication is an asynchronous operation, even for returning users.
- Never build a protected route system in React without a tri-state auth variable: `User Object`, `Null` (logged out), and `Undefined / Loading` (checking).
Still Stuck With This Issue?
Send your exact error message or deployment issue. I'll respond with a targeted fix.
Need a Deeper Fix?
Describe your full project issue below and I'll get back to you with a targeted fix.