Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MomenSherif/react-oauth/llms.txt

Use this file to discover all available pages before exploring further.

The useGoogleLogin hook gives you complete control over your sign-in UI. Use it to trigger Google’s OAuth flow from any button, link, or other interaction — without rendering Google’s default button. The hook supports two OAuth flows:

Implicit flow

Returns an access token directly. Good for client-side-only apps that call Google APIs.

Authorization code flow

Returns an authorization code. Requires a backend to exchange the code for tokens. Supports refresh tokens.

Implicit flow

The implicit flow returns an access token that you can use directly to call Google APIs.
import { useGoogleLogin } from '@react-oauth/google';

function LoginButton() {
  const login = useGoogleLogin({
    onSuccess: tokenResponse => {
      console.log(tokenResponse.access_token);
    },
    onError: errorResponse => {
      console.error(errorResponse);
    },
  });

  return <button onClick={() => login()}>Sign in with Google</button>;
}
The tokenResponse object contains:
  • access_token — use this to call Google APIs
  • expires_in — token lifetime in seconds
  • scope — space-delimited list of granted scopes
  • token_type — always "Bearer"
The implicit flow is the default. Set flow="implicit" explicitly if you prefer to be precise.

Implicit flow options

prompt — controls the consent screen behavior:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  prompt: 'consent',        // Always show consent screen
  // prompt: 'select_account', // Show account picker (default)
  // prompt: 'none',           // Silent sign-in, fails if not already authorized
  // prompt: '',               // No prompt preference
});
state — an opaque string passed through the OAuth flow. Not recommended for implicit flow; use only if you have a specific reason:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse.state),
  state: 'some-state-value',
});

Authorization code flow

The authorization code flow returns a code that your backend exchanges for access and refresh tokens.
import { useGoogleLogin } from '@react-oauth/google';

function LoginButton() {
  const login = useGoogleLogin({
    flow: 'auth-code',
    onSuccess: async codeResponse => {
      // Send the code to your backend
      const response = await fetch('/api/auth/google', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code: codeResponse.code }),
      });
      const tokens = await response.json();
      console.log(tokens);
    },
    onError: errorResponse => {
      console.error(errorResponse);
    },
  });

  return <button onClick={() => login()}>Sign in with Google</button>;
}
The codeResponse object contains:
  • code — the authorization code to exchange on your backend
  • scope — scopes the user granted

Auth code flow options

ux_mode"popup" (default) or "redirect":
const login = useGoogleLogin({
  flow: 'auth-code',
  ux_mode: 'redirect',
  redirect_uri: 'https://your-app.com/auth/google/callback',
  onSuccess: codeResponse => console.log(codeResponse),
});
When using ux_mode: "redirect", you must set redirect_uri to a URI registered in the Google Cloud Console. In redirect mode, the onSuccess callback is not invoked — Google redirects the browser directly to redirect_uri. select_account — prompt the user to pick an account even if they’re already signed in:
const login = useGoogleLogin({
  flow: 'auth-code',
  select_account: true,
  onSuccess: codeResponse => console.log(codeResponse),
});
state — recommended for redirect flow to protect against CSRF:
const login = useGoogleLogin({
  flow: 'auth-code',
  ux_mode: 'redirect',
  redirect_uri: 'https://your-app.com/auth/google/callback',
  state: 'random-csrf-token',
  onSuccess: codeResponse => console.log(codeResponse),
});

Common options for both flows

scope — request additional OAuth scopes beyond openid profile email (the defaults):
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
});
Multiple scopes are space-delimited:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  scope: 'https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/drive.readonly',
});
hint — pre-fill the account picker with a known email address:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  hint: 'user@example.com',
});
hosted_domain — restrict sign-in to a Google Workspace domain:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  hosted_domain: 'yourcompany.com',
});
enable_serial_consent — defaults to true. Set to false to disable granular permissions for apps created before 2019:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  enable_serial_consent: false,
});

Handling non-OAuth errors

Use onNonOAuthError to handle issues like the popup being blocked or closed before completing:
const login = useGoogleLogin({
  onSuccess: tokenResponse => console.log(tokenResponse),
  onNonOAuthError: nonOAuthError => {
    if (nonOAuthError.type === 'popup_closed') {
      console.log('User closed the popup');
    } else if (nonOAuthError.type === 'popup_failed_to_open') {
      console.log('Popup was blocked by the browser');
    }
  },
});

Checking granted scopes

After sign-in, verify whether the user actually granted the scopes you requested.
import { useGoogleLogin, hasGrantedAllScopesGoogle } from '@react-oauth/google';

const login = useGoogleLogin({
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  onSuccess: tokenResponse => {
    const hasCalendarAccess = hasGrantedAllScopesGoogle(
      tokenResponse,
      'https://www.googleapis.com/auth/calendar.readonly',
    );

    if (hasCalendarAccess) {
      // Proceed to call the Calendar API
    } else {
      // Ask the user to grant access
    }
  },
});
  • hasGrantedAllScopesGoogle(tokenResponse, ...scopes) — returns true only if every listed scope was granted
  • hasGrantedAnyScopeGoogle(tokenResponse, ...scopes) — returns true if at least one listed scope was granted

Real-world example with loading state

import { useState } from 'react';
import { useGoogleLogin } from '@react-oauth/google';

function LoginPage() {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const login = useGoogleLogin({
    flow: 'auth-code',
    onSuccess: async codeResponse => {
      setIsLoading(true);
      setError(null);
      try {
        const res = await fetch('/api/auth/google', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ code: codeResponse.code }),
        });
        if (!res.ok) throw new Error('Authentication failed');
        // Redirect or update app state
      } catch (err) {
        setError('Sign-in failed. Please try again.');
      } finally {
        setIsLoading(false);
      }
    },
    onError: () => {
      setError('Google sign-in was unsuccessful.');
    },
    onNonOAuthError: nonOAuthError => {
      if (nonOAuthError.type === 'popup_closed') return; // User cancelled, no error needed
      setError('Could not open sign-in popup. Check browser settings.');
    },
  });

  return (
    <div>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <button onClick={() => login()} disabled={isLoading}>
        {isLoading ? 'Signing in...' : 'Sign in with Google'}
      </button>
    </div>
  );
}