Use Supabase Auth with React
Learn how to use Supabase Auth with React.js.
Create a new Supabase project
Launch a new project 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.
SQL_EDITOR
1select * from auth.users;Create a React app
Create a React app using a Vite template.
Terminal
1npm create vite@latest my-app -- --template reactInstall the Supabase client library
Navigate to the React app and install the Supabase libraries.
Terminal
1cd my-app && npm install @supabase/supabase-jsDeclare Supabase Environment Variables
Rename .env.example to .env.local and populate with your Supabase connection variables:
Project URL
Publishable key
Anon key
.env.local
1VITE_SUPABASE_URL=your-project-url2VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=your-publishable-key-or-anon-keyYou can also get the Project URL and key from the project's Connect dialog.
Changes to API keys
Supabase is changing the way keys work to improve project security and developer experience. You can read the full announcement, but in the transition period, you can use both the current anon and service_role keys and the new publishable key with the form sb_publishable_xxx which will replace the older keys.
In most cases, you can get the correct key from the Project's Connect dialog, but if you want a specific key, you can find all keys in the API Keys section of a Project's Settings page:
- For legacy keys, copy the
anonkey for client-side operations and theservice_rolekey for server-side operations from the Legacy API Keys tab. - For new keys, open the API Keys tab, if you don't have a publishable key already, click Create new API Keys, and copy the value from the Publishable key section.
Read the API keys docs for a full explanation of all key types and their uses.
Set up your login component
Explore drop-in UI components for your Supabase app.
UI components built on shadcn/ui that connect to Supabase via a single command.
Explore ComponentsIn App.jsx, create a Supabase client using your Project URL and key.
The code uses the getClaims method in App.jsx to validate the local JWT before showing the signed-in user.
src/App.jsx
1import "./index.css";2import { useState, useEffect } from "react";3import { createClient } from "@supabase/supabase-js";45const supabase = createClient(import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY);67export default function App() {8 const [loading, setLoading] = useState(false);9 const [email, setEmail] = useState("");10 const [claims, setClaims] = useState(null);1112 // Check URL params on initial render13 const params = new URLSearchParams(window.location.search);14 const hasTokenHash = params.get("token_hash");1516 const [verifying, setVerifying] = useState(!!hasTokenHash);17 const [authError, setAuthError] = useState(null);18 const [authSuccess, setAuthSuccess] = useState(false);1920 useEffect(() => {21 // Check if we have token_hash in URL (magic link callback)22 const params = new URLSearchParams(window.location.search);23 const token_hash = params.get("token_hash");24 const type = params.get("type");2526 if (token_hash) {27 // Verify the OTP token28 supabase.auth.verifyOtp({29 token_hash,30 type: type || "email",31 }).then(({ error }) => {32 if (error) {33 setAuthError(error.message);34 } else {35 setAuthSuccess(true);36 // Clear URL params37 window.history.replaceState({}, document.title, "/");38 }39 setVerifying(false);40 });41 }4243 // Check for existing session using getClaims44 supabase.auth.getClaims().then(({ data: { claims } }) => {45 setClaims(claims);46 });4748 // Listen for auth changes49 const {50 data: { subscription },51 } = supabase.auth.onAuthStateChange(() => {52 supabase.auth.getClaims().then(({ data: { claims } }) => {53 setClaims(claims);54 });55 });5657 return () => subscription.unsubscribe();58 }, []);5960 const handleLogin = async (event) => {61 event.preventDefault();62 setLoading(true);63 const { error } = await supabase.auth.signInWithOtp({64 email,65 options: {66 emailRedirectTo: window.location.origin,67 }68 });69 if (error) {70 alert(error.error_description || error.message);71 } else {72 alert("Check your email for the login link!");73 }74 setLoading(false);75 };7677 const handleLogout = async () => {78 await supabase.auth.signOut();79 setClaims(null);80 };8182 // Show verification state83 if (verifying) {84 return (85 <div>86 <h1>Authentication</h1>87 <p>Confirming your magic link...</p>88 <p>Loading...</p>89 </div>90 );91 }9293 // Show auth error94 if (authError) {95 return (96 <div>97 <h1>Authentication</h1>98 <p>ā Authentication failed</p>99 <p>{authError}</p>100 <button101 onClick={() => {102 setAuthError(null);103 window.history.replaceState({}, document.title, "/");104 }}105 >106 Return to login107 </button>108 </div>109 );110 }111112 // Show auth success (briefly before claims load)113 if (authSuccess && !claims) {114 return (115 <div>116 <h1>Authentication</h1>117 <p>ā Authentication successful!</p>118 <p>Loading your account...</p>119 </div>120 );121 }122123 // If user is logged in, show welcome screen124 if (claims) {125 return (126 <div>127 <h1>Welcome!</h1>128 <p>You are logged in as: {claims.email}</p>129 <button onClick={handleLogout}>130 Sign Out131 </button>132 </div>133 );134 }135136 // Show login form137 return (138 <div>139 <h1>Supabase + React</h1>140 <p>Sign in via magic link with your email below</p>141 <form onSubmit={handleLogin}>142 <input143 type="email"144 placeholder="Your email"145 value={email}146 required={true}147 onChange={(e) => setEmail(e.target.value)}148 />149 <button disabled={loading}>150 {loading ? <span>Loading</span> : <span>Send magic link</span>}151 </button>152 </form>153 </div>154 );155}Customize email template
Before proceeding, change the email template to support support a server-side authentication flow that sends a token hash:
- Go to the 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 to
https://localhost:5173
Start the app
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.
Terminal
1npm run dev