My project was created using vite + react-js. I have google auth enabled in my Supabase project with Client IDs etc, already configured from Google Cloud on Supabase already.
As I tested on localhost so I have the URL whitelisted in my Supabase project as well:
Below is how I'll implement it in my ReactJS project.
Install this first:
yarn add @supabase/supabase-js
Add a file AuthContext.tsx in your lib folder, and add this code:
import { createContext, useContext, useEffect, useState } from "react";
import type { ReactNode } from "react";
import type { Session, User } from "@supabase/supabase-js";
import { supabase } from "@/lib/supabaseClient";
import { toast } from "sonner";
type UserProfile = {
name: string;
email: string;
avatarUrl: string;
};
type AuthContextValue = {
user: User | null;
session: Session | null;
profile: UserProfile | null;
isLoading: boolean;
signInWithGoogle: () => Promise<void>;
signOut: () => Promise<void>;
};
const AuthContext = createContext<AuthContextValue | undefined>(undefined);
const extractProfile = (user: User | null): UserProfile | null => {
if (!user) {
return null;
}
const metadata = user.user_metadata ?? {};
const name =
metadata.full_name ||
metadata.name ||
metadata.user_name ||
(user.email ? user.email.split("@")[0] : "Anonymous");
return {
name,
email: user.email ?? metadata.email ?? "",
avatarUrl: metadata.avatar_url || metadata.picture || "",
};
};
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [session, setSession] = useState<Session | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticating, setIsAuthenticating] = useState(false);
useEffect(() => {
const initSession = async () => {
const { data, error } = await supabase.auth.getSession();
if (error) {
console.error("Failed to get session:", error);
}
setSession(data.session ?? null);
setIsLoading(false);
};
void initSession();
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, nextSession) => {
setSession(nextSession);
setIsLoading(false);
setIsAuthenticating(false);
});
return () => {
subscription.unsubscribe();
};
}, []);
const signInWithGoogle = async () => {
try {
setIsAuthenticating(true);
const { error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: window.location.origin,
},
});
if (error) {
setIsAuthenticating(false);
toast.error("Unable to sign in with Google.");
console.error("Error signing in:", error);
return;
}
setIsAuthenticating(false);
} catch (error) {
setIsAuthenticating(false);
console.error("Unexpected error signing in:", error);
toast.error("Unexpected error signing in.");
}
};
const signOut = async () => {
try {
setIsAuthenticating(true);
const { error } = await supabase.auth.signOut();
if (error) {
setIsAuthenticating(false);
toast.error("Unable to sign out.");
console.error("Error signing out:", error);
return;
}
setIsAuthenticating(false);
toast.success("Signed out.");
} catch (error) {
setIsAuthenticating(false);
console.error("Unexpected error signing out:", error);
toast.error("Unexpected error signing out.");
}
};
const user = session?.user ?? null;
const value: AuthContextValue = {
user,
session,
profile: extractProfile(user),
isLoading: isLoading || isAuthenticating,
signInWithGoogle,
signOut,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
};
Add another file named supabaseClient.ts and in it add:
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.YOUR_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.YOUR_SUPABASE_ANON_KEY;
if (!supabaseUrl || !supabaseAnonKey) {
throw new Error("Missing Supabase environment variables. Please set YOUR_PUBLIC_SUPABASE_URL and YOUR_SUPABASE_ANON_KEY.");
}
export const supabase = createClient(supabaseUrl, supabaseAnonKey);


Top comments (0)