--- title: 'Use Supabase Auth with React' subtitle: 'Learn how to use Supabase Auth with React.js.' breadcrumb: 'Auth Quickstarts' hideToc: true --- [Launch a new project](/dashboard) in the Supabase Dashboard. Your new database has a table for storing your users. You can see that this table is currently empty by running some SQL in the [SQL Editor](/dashboard/project/_/sql). ```sql name=SQL_EDITOR select * from auth.users; ```` Create a React app using a [Vite](https://vitejs.dev/guide/) template. ```bash name=Terminal npm create vite@latest my-app -- --template react ``` Navigate to the React app and install the Supabase libraries. ```bash name=Terminal cd my-app && npm install @supabase/supabase-js ``` Rename `.env.example` to `.env.local` and populate with your Supabase connection variables: ```text name=.env.local VITE_SUPABASE_URL=your-project-url VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=sb_publishable_... or anon key ``` <$Partial path="api_settings_steps.mdx" variables={{ "framework": "react", "tab": "frameworks" }} /> <$Partial path="uiLibCta.mdx" /> In `App.jsx`, create a Supabase client using your Project URL and key. You can configure the Auth component to display whenever there is no session inside `supabase.auth.getSession()` ```jsx name=src/App.jsx import "./index.css"; import { useState, useEffect } from "react"; import { createClient } from "@supabase/supabase-js"; const supabase = createClient(import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY); export default function App() { const [loading, setLoading] = useState(false); const [email, setEmail] = useState(""); const [session, setSession] = useState(null); // Check URL params on initial render const params = new URLSearchParams(window.location.search); const hasTokenHash = params.get("token_hash"); const [verifying, setVerifying] = useState(!!hasTokenHash); const [authError, setAuthError] = useState(null); const [authSuccess, setAuthSuccess] = useState(false); useEffect(() => { // Check if we have token_hash in URL (magic link callback) const params = new URLSearchParams(window.location.search); const token_hash = params.get("token_hash"); const type = params.get("type"); if (token_hash) { // Verify the OTP token supabase.auth.verifyOtp({ token_hash, type: type || "email", }).then(({ error }) => { if (error) { setAuthError(error.message); } else { setAuthSuccess(true); // Clear URL params window.history.replaceState({}, document.title, "/"); } setVerifying(false); }); } // Check for existing session supabase.auth.getSession().then(({ data: { session } }) => { setSession(session); }); // Listen for auth changes const { data: { subscription }, } = supabase.auth.onAuthStateChange((_event, session) => { setSession(session); }); return () => subscription.unsubscribe(); }, []); const handleLogin = async (event) => { event.preventDefault(); setLoading(true); const { error } = await supabase.auth.signInWithOtp({ email, options: { emailRedirectTo: window.location.origin, } }); if (error) { alert(error.error_description || error.message); } else { alert("Check your email for the login link!"); } setLoading(false); }; const handleLogout = async () => { await supabase.auth.signOut(); setSession(null); }; // Show verification state if (verifying) { return (

Authentication

Confirming your magic link...

Loading...

); } // Show auth error if (authError) { return (

Authentication

✗ Authentication failed

{authError}

); } // Show auth success (briefly before session loads) if (authSuccess && !session) { return (

Authentication

✓ Authentication successful!

Loading your account...

); } // If user is logged in, show welcome screen if (session) { return (

Welcome!

You are logged in as: {session.user.email}

); } // Show login form return (

Supabase + React

Sign in via magic link with your email below

setEmail(e.target.value)} />
); } ```
Before proceeding, change the email template to support support a server-side authentication flow that sends a token hash: - Go to the [Auth templates](/dashboard/project/_/auth/templates) page in your dashboard. - Select the Confirm sign up template. - Change `{{ .ConfirmationURL }}` to `{{ .SiteURL }}?token_hash={{ .TokenHash }}&type=email`. - Change your [Site URL](/dashboard/project/_/auth/url-configuration) to `https://localhost:5173` Start the app, go to http://localhost:5173 in a browser, and open the browser console and you should be able to register and log in. ```bash name=Terminal npm run dev ```