How to Implement React Google Login with Firebase Auth: Best Practices and Common Pitfalls
The most reliable way to implement React Google login is using the Firebase Auth modular SDK with signInWithPopup triggered directly from a user click handler, while avoiding common pitfalls like popup blockers, SSR mismatches, and mixing compat/modular SDK versions.
Firebase Auth provides a robust, client-side authentication solution for React applications. According to the firebase/firebase-js-sdk source code, the Google login implementation centers around the GoogleAuthProvider class and browser-specific sign-in strategies that handle OAuth token exchange without requiring a backend server.
Understanding the Firebase Auth Architecture for Google Login
The GoogleAuthProvider Class
The GoogleAuthProvider is a specialized OAuth provider that configures the Google-specific authentication parameters. In packages/auth/src/core/providers/google.ts, this class extends the generic BaseOAuthProvider and sets the provider ID to "google.com".
When you instantiate new GoogleAuthProvider(), you create a configuration object that can be passed to sign-in methods. The class also provides helper methods like addScope() to request additional Google API permissions (e.g., Google Drive or Calendar access).
Sign-In Strategies: Popup vs. Redirect
Firebase Auth offers two primary browser-based strategies for Google authentication:
signInWithPopup (implemented in packages/auth/src/platform_browser/strategies/popup.ts) opens a new browser window for the OAuth flow. This provides the best user experience because it keeps your React application state intact. However, it requires a direct user gesture to avoid popup blockers and will fail during server-side rendering (SSR).
signInWithRedirect performs a full-page navigation to Google's OAuth endpoint and returns to your application afterward. This works in environments where popups are blocked or unavailable, but it requires handling the redirect result on page load using getRedirectResult.
Implementing React Google Login: Step-by-Step Code Examples
Basic Implementation with signInWithPopup
This example demonstrates the minimal implementation using the modular SDK. The critical requirement is calling signInWithPopup directly within an event handler to satisfy browser autoplay and popup policies.
import { initializeApp } from "firebase/app";
import {
getAuth,
GoogleAuthProvider,
signInWithPopup,
onAuthStateChanged,
User,
} from "firebase/auth";
import { useEffect, useState } from "react";
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const provider = new GoogleAuthProvider();
export default function GoogleLogin() {
const [user, setUser] = useState<User | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (u) => setUser(u));
return () => unsubscribe();
}, []);
const handleLogin = async () => {
try {
await signInWithPopup(auth, provider);
} catch (e: any) {
setError(e.message);
}
};
if (user) {
return <div>Welcome, {user.displayName}</div>;
}
return (
<>
<button onClick={handleLogin}>Sign in with Google</button>
{error && <p style={{ color: "red" }}>{error}</p>}
</>
);
}
Custom Hook for Reusable Auth Logic
For larger applications, encapsulate Firebase Auth logic in a custom hook. This pattern centralizes error handling, loading states, and persistence configuration.
import {
getAuth,
GoogleAuthProvider,
signInWithPopup,
onAuthStateChanged,
User,
setPersistence,
browserLocalPersistence,
} from "firebase/auth";
import { useEffect, useState, useCallback } from "react";
export function useFirebaseAuth() {
const auth = getAuth();
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const provider = new GoogleAuthProvider();
useEffect(() => {
setPersistence(auth, browserLocalPersistence).catch(console.error);
const unsub = onAuthStateChanged(auth, (u) => {
setUser(u);
setLoading(false);
});
return () => unsub();
}, [auth]);
const signIn = useCallback(async () => {
setError(null);
try {
await signInWithPopup(auth, provider);
} catch (e: any) {
setError(e.message);
}
}, [auth, provider]);
const signOut = useCallback(() => auth.signOut(), [auth]);
return { user, loading, error, signIn, signOut };
}
Handling Redirect Flows for SSR
When using signInWithRedirect (necessary for some mobile browsers or SSR environments), you must handle the OAuth result when the user returns to your application.
import {
getAuth,
GoogleAuthProvider,
signInWithRedirect,
getRedirectResult,
} from "firebase/auth";
import { useEffect } from "react";
export default function LoginPage() {
const auth = getAuth();
const provider = new GoogleAuthProvider();
const startRedirect = () => signInWithRedirect(auth, provider);
useEffect(() => {
if (typeof window !== "undefined") {
getRedirectResult(auth)
.then((result) => {
if (result?.user) {
console.log("Redirect sign-in successful:", result.user);
}
})
.catch((error) => {
console.error("Redirect sign-in error:", error);
});
}
}, [auth]);
return <button onClick={startRedirect}>Sign in with Google</button>;
}
Common Pitfalls to Avoid
When implementing React Google login with Firebase Auth, developers frequently encounter these specific issues:
Missing authDomain in Firebase Config
The OAuth redirect URI is constructed using the authDomain value. If this field is omitted from your Firebase configuration object, the Google sign-in flow will fail immediately with an invalid request error. Always verify that your config includes authDomain copied directly from the Firebase console.
Google Sign-In Method Not Enabled
Firebase Auth requires explicit enablement of identity providers in the Firebase Console. Navigate to Authentication → Sign-in method and ensure Google is enabled. Attempting to use GoogleAuthProvider without this configuration results in an auth/operation-not-allowed error.
Popup Blockers and User Gestures
Browsers block window.open calls that are not triggered by a direct user interaction. If you wrap signInWithPopup in a setTimeout or call it after an await for unrelated async work, the browser will treat it as an unsolicited popup. Always invoke signInWithPopup synchronously within an onClick handler.
Server-Side Rendering (SSR) Mismatches
The signInWithPopup method relies on the window object and DOM APIs that do not exist in Node.js environments. If you call this method during server-side rendering (e.g., in Next.js without useEffect guards), your application will crash with window is not defined. Use typeof window !== "undefined" checks or prefer signInWithRedirect for SSR-compatible flows.
Mixing Compat and Modular SDKs
Firebase provides a "compat" SDK for legacy migration and a "modular" SDK (v9+) for tree-shaking. Importing firebase/compat/auth alongside firebase/auth creates duplicate Auth instances and type conflicts. Standardize on the modular SDK: import { getAuth, GoogleAuthProvider } from "firebase/auth".
Credential Expiration Handling
Google OAuth access tokens expire after one hour. Storing these tokens in local state for API calls will lead to 401 errors. Rely on Firebase Auth's automatic token refresh mechanism via getIdToken() rather than caching the OAuth credential yourself.
Best Practices for Production React Apps
Use the Modular SDK with Async/Await
The Firebase v9 modular SDK enables tree-shaking and significantly reduces bundle size. Always use async/await syntax with signInWithPopup for cleaner error handling compared to promise chains.
Centralize Auth State with React Context
Create an AuthProvider component that wraps onAuthStateChanged and provides the current user to your component tree. This prevents prop drilling and ensures consistent auth state across routes.
Implement Graceful Error Handling
Wrap authentication calls in try-catch blocks and map Firebase error codes (e.g., auth/popup-closed-by-user, auth/cancelled-popup-request) to user-friendly messages.
Configure Auth Persistence Explicitly
While Firebase defaults to indexedDB for persistence, explicitly set your preferred persistence mode using setPersistence(auth, browserLocalPersistence) to ensure consistent behavior across browsers and incognito modes.
Unsubscribe from Listeners
Always return the unsubscribe function from onAuthStateChanged in your useEffect cleanup function to prevent memory leaks when components unmount.
Consider ReactFire for Rapid Development For teams looking to reduce boilerplate, the ReactFire library provides pre-built hooks and components that wrap Firebase Auth with React best practices already implemented.
Summary
- Use
GoogleAuthProviderfrom the modular SDK to configure Google OAuth parameters, as implemented inpackages/auth/src/core/providers/google.ts. - Trigger
signInWithPopupdirectly from user click handlers to avoid browser popup blockers, referencing the implementation inpackages/auth/src/platform_browser/strategies/popup.ts. - Guard against SSR by checking
typeof window !== "undefined"before calling browser-specific auth methods. - Enable Google sign-in in the Firebase Console and verify
authDomainis present in your config to preventauth/operation-not-allowederrors. - Manage auth state with
onAuthStateChangedlisteners initialized viapackages/auth/src/core/auth/initialize.ts, and always unsubscribe in cleanup functions. - Avoid mixing compat and modular SDKs to prevent duplicate instances and type conflicts.
Frequently Asked Questions
Why does my Google popup close immediately or get blocked by the browser?
Browsers block popups that are not triggered by a direct user gesture. If you call signInWithPopup inside a setTimeout, after an await for unrelated data fetching, or conditionally after a state update, the browser loses the connection to the click event. Always invoke signInWithPopup synchronously within the onClick handler, as shown in the popup.ts implementation.
How do I handle Google authentication in Next.js or other SSR frameworks?
Server-side rendering executes in a Node.js environment where window is undefined. Calling signInWithPopup during SSR causes a runtime error. You have two solutions: First, guard the call with if (typeof window !== "undefined") and only render the login button on the client. Second, use signInWithRedirect instead, which works across environments, and handle the result with getRedirectResult in a useEffect hook after the page loads.
What is the difference between the modular and compat Firebase SDK, and which should I use for React?
The modular SDK (v9+) uses a functional, tree-shakable architecture where you import individual functions like getAuth and signInWithPopup. The compat SDK maintains the v8 namespace-based API for migration purposes. Mixing them creates duplicate Auth instances and type conflicts. For modern React applications, always use the modular SDK: import { getAuth, GoogleAuthProvider } from "firebase/auth". This reduces bundle size and aligns with the implementation in packages/auth/index.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →