# How to Implement React Google Login with Firebase Auth: Best Practices and Common Pitfalls

> Master React Google login with Firebase Auth best practices. Avoid common pitfalls like popup blockers and SSR issues for seamless authentication.

- Repository: [Firebase/firebase-js-sdk](https://github.com/firebase/firebase-js-sdk)
- Tags: best-practices
- Published: 2026-02-16

---

**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`](https://github.com/firebase/firebase-js-sdk/blob/main/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`](https://github.com/firebase/firebase-js-sdk/blob/main/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.

```typescript
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.

```typescript
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.

```typescript
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 `GoogleAuthProvider`** from the modular SDK to configure Google OAuth parameters, as implemented in [`packages/auth/src/core/providers/google.ts`](https://github.com/firebase/firebase-js-sdk/blob/main/packages/auth/src/core/providers/google.ts).
- **Trigger `signInWithPopup`** directly from user click handlers to avoid browser popup blockers, referencing the implementation in [`packages/auth/src/platform_browser/strategies/popup.ts`](https://github.com/firebase/firebase-js-sdk/blob/main/packages/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 `authDomain` is present in your config to prevent `auth/operation-not-allowed` errors.
- **Manage auth state** with `onAuthStateChanged` listeners initialized via [`packages/auth/src/core/auth/initialize.ts`](https://github.com/firebase/firebase-js-sdk/blob/main/packages/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`](https://github.com/firebase/firebase-js-sdk/blob/main/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`](https://github.com/firebase/firebase-js-sdk/blob/main/packages/auth/index.ts).