在 React 中使用 Supabase 认证
Learn how to use Supabase Auth with React.js.
快速入门 #
🌐 Quickstart
1
2
Create a React app
使用 Vite 模板创建一个 React 应用。
Terminal
npm create vite@latest my-app -- --template react3
Install the Supabase client library
进入 React 应用并安装 Supabase 库。
Terminal
cd my-app && npm install @supabase/supabase-js4
Declare Supabase Environment Variables
将 .env.example 重命名为 .env.local 并填写你的 Supabase 连接变量:
获取 API 详情 #
🌐 Get API details
要与数据库表中的数据进行交互,你可以使用封装了自动生成的数据 API 端点的客户端库,并使用来自项目 Connect 对话框的项目 URL 和密钥进行认证。
🌐 To interact with data in database tables, you use the client libraries that wrap the auto-generated Data API endpoints, authenticating using the Project URL and key from the project Connect dialog.
.env.local
VITE_SUPABASE_URL=your-project-urlVITE_SUPABASE_PUBLISHABLE_KEY=your-publishable-key-or-anon-key5
Set up your sign-in component
探索适用于你 Supabase 应用的即插即用 UI 组件。
基于 shadcn/ui 构建的 UI 组件,通过一个命令连接到 Supabase。
🌐 UI components built on shadcn/ui that connect to Supabase via a single command.
在 App.jsx 中,使用你的项目 URL 和密钥创建一个 Supabase 客户端。
这段代码在 App.jsx 中使用 getClaims 方法来验证本地 JWT,然后再显示已登录的用户。
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_KEY)export default function App() { const [loading, setLoading] = useState(false) const [email, setEmail] = useState('') const [claims, setClaims] = 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 using getClaims supabase.auth.getClaims().then(({ data: { claims } }) => { setClaims(claims) }) // Listen for auth changes const { data: { subscription }, } = supabase.auth.onAuthStateChange(() => { supabase.auth.getClaims().then(({ data: { claims } }) => { setClaims(claims) }) }) 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() setClaims(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 claims load) if (authSuccess && !claims) { return ( <div> <h1>Authentication</h1> <p>✓ Authentication successful!</p> <p>Loading your account...</p> </div> ) } // If user is logged in, show welcome screen if (claims) { return ( <div> <h1>Welcome!</h1> <p>You are logged in as: {claims.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