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.
hasGrantedAllScopesGoogle
hasGrantedAnyScopeGoogle
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 >
);
}