Auth

Use Supabase Auth with React

Learn how to use Supabase Auth with React.js.


1

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
1
select * from auth.users;
2

Create a React app

Create a React app using a Vite template.

Terminal
1
npm create vite@latest my-app -- --template react
3

Install the Supabase client library

Navigate to the React app and install the Supabase libraries.

Terminal
1
cd my-app && npm install @supabase/supabase-js
4

Declare Supabase Environment Variables

Create .env.local and populate with your project's URL and Key.

.env.local
1
2
VITE_SUPABASE_URL=your-project-urlVITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=sb_publishable_... or anon key
5

Set up your login component

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()

src/App.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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 ( <div> <h1>Authentication</h1> <p>Confirming your magic link...</p> <p>Loading...</p> </div> ); } // Show auth error if (authError) { return ( <div> <h1>Authentication</h1> <p>✗ Authentication failed</p> <p>{authError}</p> <button onClick={() => { setAuthError(null); window.history.replaceState({}, document.title, "/"); }} > Return to login </button> </div> ); } // Show auth success (briefly before session loads) if (authSuccess && !session) { return ( <div> <h1>Authentication</h1> <p>✓ Authentication successful!</p> <p>Loading your account...</p> </div> ); } // If user is logged in, show welcome screen if (session) { return ( <div> <h1>Welcome!</h1> <p>You are logged in as: {session.user.email}</p> <button onClick={handleLogout}> Sign Out </button> </div> ); } // Show login form return ( <div> <h1>Supabase + React</h1> <p>Sign in via magic link with your email below</p> <form onSubmit={handleLogin}> <input type="email" placeholder="Your email" value={email} required={true} onChange={(e) => setEmail(e.target.value)} /> <button disabled={loading}> {loading ? <span>Loading</span> : <span>Send magic link</span>} </button> </form> </div> );}
6

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
7

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
1
npm run dev