Skip to main content
Google One-Tap is the little popup card that appears top-right when a signed-out visitor lands on your home page and they have an active Google session. One click and they’re signed in — no redirect, no form. vibestrap wires it via Better Auth’s One-Tap plugin and a tiny client component (src/components/auth/one-tap.tsx). It uses FedCM, Google’s newer browser API that doesn’t depend on third-party cookies — which means it’ll keep working as Chrome phases those out.

Prerequisites

  • Google OAuth already set up. One-Tap reuses the same OAuth client, so GOOGLE_CLIENT_ID must already exist.
  • A NEXT_PUBLIC_GOOGLE_CLIENT_ID env var — the public mirror of the same ID.
  • HTTPS in production. (HTTP works on localhost for dev.)

Step-by-step setup

  1. Mirror the client ID into the public env. Better Auth’s One-Tap plugin needs the ID on the client too — add to .env.local:
    NEXT_PUBLIC_GOOGLE_CLIENT_ID=1234567890-abc...apps.googleusercontent.com
    
    Same value as GOOGLE_CLIENT_ID. It’s safe to expose; it’s a public identifier.
  2. Turn the feature on. In src/config/site.ts:
    features: { enableOneTap: true, /* ... */ }
    
  3. Confirm the component is mounted. It already lives in src/app/layout.tsx (or the locale layout) inside the NextIntlClientProvider so it’s alive across every page. The component renders nothing — it just listens for an idle moment to call authClient.oneTap().
  4. Restart pnpm dev. Public env vars are baked at build/dev start.

How it actually works

src/components/auth/one-tap.tsx is a 30-line 'use client' component:
const { data: session, isPending } = useSession();
useEffect(() => {
  if (isPending || session) return;
  authClient.oneTap?.().catch(() => {});
}, [isPending, session]);
If the user is signed in, it does nothing. If not, it asks Better Auth’s plugin to prompt Google. Errors are swallowed because every reason a One-Tap can fail (ineligible browser, dismissed, no Google session) is non-actionable for the user. The “Continue with Google” button on /login and the One-Tap popup are independent — both can be active at the same time. One-Tap just makes the home page lighter. The matching client config is in src/lib/auth-client.ts:
oneTapClient({
  clientId: googleClientId,
  autoSelect: false,         // user must click — never auto-pick
  cancelOnTapOutside: true,  // dismissable
  context: 'signin',
})
autoSelect: false is deliberate — auto-signing someone in without their click is exactly the kind of dark pattern you want to avoid, and Google penalises it on accounts.google.com. Leave it off.

Verify it works

  1. Make sure you have a Google account signed in (in another tab) and you are not signed in to your vibestrap site.
  2. Open the home page in an incognito window with that Google session present.
  3. Within a second or two, the One-Tap card should slide in from the top-right.
  4. Click it — you’re signed in, no redirect.
If nothing appears, open DevTools → Network → filter for accounts.google.com to see whether the FedCM request was sent.

Common pitfalls

  • Works locally, dead in production. Almost always missing NEXT_PUBLIC_GOOGLE_CLIENT_ID in your hosting provider’s env config (Vercel, Netlify, etc.). Public env vars are baked at build time — set them, then redeploy.
  • HTTPS required. Production hosts must serve over HTTPS. localhost is the one HTTP exception browsers allow.
  • Brave or Safari shows nothing. FedCM is gated behind a setting in Brave (Shields) and is still rolling out in Safari. Don’t treat the One-Tap as the only way in — keep the regular button.
  • User has multiple Google accounts. Instead of an instant sign-in card, they see an account chooser. Same plugin, just a different UI from Google’s side — nothing to fix on your end.
  • Card appears but click does nothing. Usually means your OAuth client’s authorized JavaScript origins don’t include the current host. Add it in the Google Cloud Console under your OAuth 2.0 Client ID → Authorized JavaScript origins.

Official docs