In this tutorial, we'll walk through implementing Google Login in a Vite + React app using @react-oauth/google. The whole flow will be handled client-side only — no backend required.
We'll start by setting up the Google Cloud Console with OAuth 2.0 credentials, then integrate the login button using the official Google Identity Services SDK for React. You'll learn how to:
- Authenticate users via a Google popup window
- Obtain and decode the ID token (JWT) that contains user profile information
- Store the token securely on the client (localStorage, sessionStorage, or memory)
- Use the decoded payload to access user info like name, email, and profile picture
- Optionally log out the user and clear session data
# Step 1: Google Cloud Console Setup
- Go to Google Cloud Console
- Create a new project
- Navigate to OAuth Consent Screen and configure it:
- Go to Credentials > Create Credentials > OAuth 2.0 Client ID:
http://localhost:5173 to Authorized JavaScript Origins (and if you plan to host your app, make sure to also include your production URL, e.g., https://codewithseb.com)
- Redirect URI can be the same or left blank for popup-based flow
- Copy your Client ID
To install the required packages using npm:
npm install @react-oauth/google jwt-decodeAlternatively, you can use yarn:
yarn add @react-oauth/google jwt-decodeOr pnpm:
pnpm add @react-oauth/google jwt-decode# Step 3: Configure Provider
Create a .env file in your project root:
VITE_GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.comWrap your React app with GoogleOAuthProvider in main.jsx:
// main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { GoogleOAuthProvider } from '@react-oauth/google'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(
<GoogleOAuthProvider clientId={import.meta.env.VITE_GOOGLE_CLIENT_ID}>
<App />
</GoogleOAuthProvider>
)# Step 4: Create Google Login Button
The example above represents a minimalistic, boilerplate implementation of Google Login using the @react-oauth/google library. It directly uses the GoogleLogin component to render the sign-in button, and handles the onSuccess callback by decoding the returned JWT token using jwt-decode, logging user data to the console, and storing the raw token in localStorage. This is helpful for getting started quickly and verifying that the login flow works, but it doesn’t scale well in real-world applications.
There are several reasons why this implementation is intentionally basic. It skips separation of concerns, has hardcoded storage logic, no type safety, and lacks any abstraction layer. It’s meant to be simple and readable — ideal for a tutorial checkpoint. However, in a production-ready app, you'd want to abstract token handling (e.g., into a hook), avoid storing sensitive data in localStorage, and add mechanisms for logout, token expiration handling, and user state sharing across components. In the next section, we’ll build a more modular and secure approach using TypeScript and session-safe patterns.
Create a LoginButton.jsx component:
// LoginButton.jsx
import { GoogleLogin } from '@react-oauth/google'
import jwt_decode from 'jwt-decode'
export default function LoginButton() {
const handleSuccess = (credentialResponse) => {
const token = credentialResponse.credential
const user = jwt_decode(token)
console.log('User data:', user)
localStorage.setItem('googleToken', token)
}
const handleError = () => {
console.error('Login Failed')
}
return <GoogleLogin onSuccess={handleSuccess} onError={handleError} />
}Usage in App.jsx:
// App.jsx
import LoginButton from './LoginButton'
export default function App() {
return (
<div>
<h1>Login with Google</h1>
<LoginButton />
</div>
)
}# Step 5: Token Handling (JWT)
After a successful login, Google returns an ID token in the form of a JWT (JSON Web Token). This token is a signed, Base64-encoded string that contains essential user identity information such as the user's Google ID (sub), email address, name, and profile picture. Because it's signed by Google, you can trust its contents as long as it hasn’t expired. The token can be decoded locally on the frontend and used to personalize the UI or conditionally control access — without needing to make additional requests to Google APIs.