Firebase Google sign-in with Next.js (App Router, Vercel)

Updated 26 Oct 2026 · Pewterkit Guides

In the Next.js App Router, run Firebase Auth inside a Client Component ('use client'), give it config through NEXT_PUBLIC_ variables, and add every host you sign in from to Firebase's authorized domains. To make Google's sign-in screen show your own domain on Vercel, set authDomain to that domain and rewrite /__/auth/* to YOUR-PROJECT.firebaseapp.com in next.config.js.

Why Google sign-in breaks in Next.js

Most Next.js + Firebase sign-in failures come from one of four places:

  • Server vs. client code. App Router components are Server Components unless a file starts with 'use client'. Even Client Components are prerendered to HTML during next build, where browser APIs such as window don't exist, so popup and redirect calls must run from an event handler or an effect, never at module or render time.
  • Build-time env vars. Next.js inlines NEXT_PUBLIC_ values into the browser bundle when next build runs. If the variable wasn't set in Vercel at build time, or you read it with a dynamic lookup such as process.env[name], the browser gets undefined and Firebase can't start.
  • Unlisted domains. Firebase only accepts sign-in from authorized domains. Projects created after April 28, 2025 no longer get localhost by default, and each Vercel deployment URL is a separate host.
  • Redirect sign-in and third-party storage. signInWithRedirect() relies on a cross-origin iframe on your auth domain. Firebase says one of its redirect workarounds is required on Chrome M115+ (from June 24, 2024), Firefox 109+ and Safari 16.1+.

Step-by-step fix

1. Put the Firebase web config in .env.local

# .env.local  (project root, not /src)
NEXT_PUBLIC_FIREBASE_API_KEY=your-web-api-key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=YOUR-PROJECT.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=YOUR-PROJECT
NEXT_PUBLIC_FIREBASE_APP_ID=1:000000000000:web:abc123

Firebase says API keys restricted to Firebase services don't need to be treated as secrets; your data is protected by Security Rules and App Check. Add the same four variables in Vercel's project settings for each environment you deploy (Production, Preview, Development). Vercel applies env var changes only to new deployments, and Next.js freezes NEXT_PUBLIC_ values at build time, so redeploy after any change.

2. Create one Firebase module for client code

// lib/firebase.ts
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  // Write each variable out in full: Next.js does not inline dynamic lookups
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

// Reuse the existing app after hot reloads instead of creating another one
export const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
export const auth = getAuth(app);

Import this file only from Client Components. Anything a 'use client' file imports becomes part of the client bundle, so the module doesn't need its own directive.

3. Add a sign-in Client Component

// app/components/GoogleSignIn.tsx
'use client';

import { useEffect, useState } from 'react';
import {
  GoogleAuthProvider,
  onAuthStateChanged,
  signInWithPopup,
  signOut,
  type User,
} from 'firebase/auth';
import { auth } from '../../lib/firebase';

export default function GoogleSignIn() {
  const [user, setUser] = useState<User | null>(null);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    // onAuthStateChanged returns an unsubscribe function; React calls it on unmount
    return onAuthStateChanged(auth, (u) => {
      setUser(u);
      setReady(true);
    });
  }, []);

  async function handleSignIn() {
    // Call the popup straight from the click handler
    await signInWithPopup(auth, new GoogleAuthProvider());
  }

  if (!ready) return null; // avoids a signed-out flash before Firebase restores the session

  return user ? (
    <button onClick={() => signOut(auth)}>Sign out {user.displayName}</button>
  ) : (
    <button onClick={handleSignIn}>Sign in with Google</button>
  );
}

Render it from any Server Component, for example app/page.tsx, with import GoogleSignIn from './components/GoogleSignIn'. The observer matters because auth.currentUser can be null while Firebase is still initializing. If Server Components also need the user, Firebase's own Next.js codelab copies the ID token into a __session cookie and reads it on the server with initializeServerApp().

Make sure Google is turned on in the Firebase console under Security > Authentication > Sign-in method (as of Oct 2026; older guides say Build > Authentication).

4. Authorize your Next.js hosts

In the Firebase console, open Security > Authentication > Settings > Authorized domains (as of Oct 2026; older guides: Build > Authentication > Settings) and click Add domain for:

  • localhost, if you sign in from next dev (served at http://localhost:3000 by default). Firebase recommends against keeping localhost in a production project.
  • Your production domain, for example app.example.com.
  • Your *.vercel.app production URL, if people use it. Each preview URL you sign in from needs its own entry, and the list doesn't take wildcards; see the guides linked below.

5. Show your own domain on Google's sign-in screen (Vercel)

Firebase's docs say users may see Continue to: https://YOUR-PROJECT.firebaseapp.com while signing in, because the SDK opens its handler at https://<authDomain>/__/auth/handler. To move that handler onto your domain, Firebase's redirect best-practices page (option 3) says to forward /__/auth/ on your domain to YOUR-PROJECT.firebaseapp.com, transparently (a 302 redirect won't work), and then change authDomain. On Next.js that forwarding is a rewrite.

5a. Add the rewrite to next.config.js:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/__/auth/:path*',
        destination: 'https://YOUR-PROJECT.firebaseapp.com/__/auth/:path*',
      },
    ];
  },
};

module.exports = nextConfig;

Test this on a preview deployment before relying on it.

(With next.config.mjs or next.config.ts, use export default nextConfig.) Next.js says rewrites act as a URL proxy and keep the browser's URL unchanged, which matches the transparent forwarding Firebase asks for. An external rewrite in vercel.json with the same source and destination is the alternative on Vercel.

Two Next.js-specific traps:

  • proxy.ts (called middleware.ts before Next.js 16). It runs before next.config.js rewrites, and without a matcher it runs on every request. If it redirects signed-out visitors, exclude the auth path, for example matcher: ['/((?!api|_next/static|_next/image|__/auth|favicon.ico).*)'].
  • output: 'export'. Static exports don't support rewrites. Put the proxy on your static host instead (for Firebase Hosting, see the custom-domain guide below).

5b. Register the new redirect URI. In the Google Cloud console, open APIs & Services > Credentials, pick the web client under OAuth 2.0 Client IDs (as of Oct 2026 the same clients are also listed under Google Auth Platform > Clients), and add:

https://app.example.com/__/auth/handler

Google says changes can take 5 minutes to a few hours to apply, and redirect URIs can't contain wildcards.

5c. Point authDomain at your domain in Production only. In Vercel, set NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=app.example.com for the Production environment and leave Preview and Development on YOUR-PROJECT.firebaseapp.com, since preview hosts don't have a registered handler URI. Confirm app.example.com is in Firebase's authorized domains, then redeploy.

How to confirm it worked

  1. Open https://app.example.com/__/auth/handler in a browser. You should get a response from Firebase, not your Next.js 404 page. A 404 means the rewrite isn't deployed or proxy.ts caught the path.
  2. Open DevTools > Network, click Sign in with Google, and check that the popup starts at https://app.example.com/__/auth/handler?....
  3. On Google's account chooser, the "continue to" line should name app.example.com.
  4. After choosing an account, your component should show Sign out with the user's name. A redirect_uri_mismatch page means step 5b hasn't applied yet or the URI has a typo.

FAQ

Do I need 'use client' in every file that uses Firebase?

No. Next.js only needs 'use client' at the top of files whose components are rendered directly from Server Components. Modules those files import, such as lib/firebase.ts, are bundled for the client automatically.

Why is my Firebase config undefined on Vercel when it works locally?

.env.local only exists on your machine. Add the NEXT_PUBLIC_ variables in Vercel's project settings for the right environment and redeploy, because Next.js inlines them at build time and Vercel applies changes only to new deployments.

Should I use signInWithPopup or signInWithRedirect in Next.js?

Popup sign-in is the simpler choice, and Firebase lists switching to it as one way to avoid the third-party storage problem. If you use signInWithRedirect(), set up the /__/auth rewrite and custom authDomain above, and call getRedirectResult(auth) in an effect on the page users return to.

Can Server Components read the signed-in Firebase user?

Not directly, because Firebase Auth state lives in the browser. Firebase's Next.js codelab sends the ID token to the server in a __session cookie and rebuilds the user with initializeServerApp() and authStateReady().

Does the /__/auth rewrite work with output: 'export'?

No. Next.js lists rewrites as unsupported for static exports, so configure the proxy on the host that serves your exported files.

Sources

  1. Authenticate Using Google with JavaScript | Firebase Authentication
  2. Best practices for using signInWithRedirect on browsers that block third-party storage access | Firebase
  3. Manage Users in Firebase
  4. Authentication FAQ and troubleshooting | Firebase Authentication
  5. Learn about and manage API keys for Firebase
  6. @firebase/app | Firebase JavaScript API reference
  7. Integrate Firebase with a Next.js app | Firebase codelab
  8. firebase-js-sdk: packages/auth/src/core/util/handler.ts
  9. use client | Next.js
  10. How to use environment variables in Next.js
  11. rewrites | Next.js
  12. proxy.js | Next.js
  13. How to create a static export of your Next.js application
  14. next CLI | Next.js
  15. Rewrites on Vercel
  16. Environment variables | Vercel
  17. Manage OAuth Clients | Google Cloud

How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked 26 Oct 2026. Spotted something out of date? Tell us.

Related guides