docs: Remove leftover auth-ui components (#45931)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Updated authentication guide for Kotlin Compose Multiplatform with
revised dependency configuration.
* Enhanced user-management example README with improved project
structure, deployment steps, and resources.

* **Chores**
* Refactored example applications to use custom authentication forms
instead of pre-built Auth UI components.
* Removed unused authentication UI dependencies from multiple example
projects.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45931)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cemal Kılıç <cemalkilic@users.noreply.github.com>
This commit is contained in:
Chris ChinchillaandCemal Kılıç authored and GitHub committed 2026-05-14 15:42:09 +00:00
1 parent 5f32b3ec6b
commit 8b01d388b9
12 files changed
+476 -75

No files matched your search

-1
View File
@@ -3,7 +3,6 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@supabase/auth-ui-react": "^0.2.1",
"@supabase/supabase-js": "^2",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.4",
+88 -5
View File
@@ -1,14 +1,97 @@
import React, { useState } from 'react'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
import JSONInput from 'react-json-editor-ajrm'
import locale from 'react-json-editor-ajrm/locale/en'
import { supabase } from './utils/supabaseClient'
import { functionsList } from './functionsList'
import { supabase } from './utils/supabaseClient'
import { useUser } from './utils/userContext'
const sampleObject = { name: 'world' }
function AuthForm() {
const [mode, setMode] = useState('sign-in')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [message, setMessage] = useState(null)
const handleSubmit = async (event) => {
event.preventDefault()
setLoading(true)
setError(null)
setMessage(null)
if (mode === 'sign-in') {
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) setError(error.message)
} else {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: { emailRedirectTo: `${window.location.origin}/` },
})
if (error) {
setError(error.message)
} else if (data.user && !data.session) {
setMessage('Check your email for a confirmation link to complete sign up.')
}
}
setLoading(false)
}
return (
<form onSubmit={handleSubmit} className="flex max-w-sm flex-col gap-3">
<label className="flex flex-col gap-1 text-sm">
Email
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="email"
className="rounded border border-gray-300 px-2 py-1"
/>
</label>
<label className="flex flex-col gap-1 text-sm">
Password
<input
type="password"
required
minLength={6}
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete={mode === 'sign-in' ? 'current-password' : 'new-password'}
className="rounded border border-gray-300 px-2 py-1"
/>
</label>
<button
type="submit"
disabled={loading}
className="rounded bg-green-500 py-2 px-4 font-bold text-white hover:bg-green-700 disabled:opacity-50"
>
{loading ? 'Loading...' : mode === 'sign-in' ? 'Sign in' : 'Sign up'}
</button>
<button
type="button"
onClick={() => {
setMode(mode === 'sign-in' ? 'sign-up' : 'sign-in')
setError(null)
setMessage(null)
}}
className="text-sm text-blue-600 underline"
>
{mode === 'sign-in' ? 'Need an account? Sign up' : 'Already have an account? Sign in'}
</button>
{error && <p className="text-sm text-red-600">{error}</p>}
{message && <p className="text-sm text-gray-700">{message}</p>}
</form>
)
}
function App() {
const { user } = Auth.useUser()
const { user } = useUser()
const [supaFunction, setSupaFunction] = useState(functionsList[0])
const [requestJson, setRequestJson] = useState(sampleObject)
const [responseJson, setResponseJson] = useState({})
@@ -43,7 +126,7 @@ function App() {
font-normal
text-gray-700
transition
ease-in-out
ease-in-out
focus:bg-white focus:text-gray-700 focus:outline-none"
onChange={(e) => setSupaFunction(e.target.value)}
>
@@ -89,7 +172,7 @@ function App() {
</button>
</div>
) : (
<Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} />
<AuthForm />
)}
</div>
</div>
+5 -4
View File
@@ -1,15 +1,16 @@
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { Auth } from '@supabase/auth-ui-react'
import { supabase } from './utils/supabaseClient'
import { UserProvider } from './utils/userContext'
ReactDOM.render(
<React.StrictMode>
<Auth.UserContextProvider supabaseClient={supabase}>
<UserProvider>
<App />
</Auth.UserContextProvider>
</UserProvider>
</React.StrictMode>,
document.getElementById('root')
)
@@ -0,0 +1,27 @@
import { createContext, useContext, useEffect, useState } from 'react'
import { supabase } from './supabaseClient'
const UserContext = createContext({ user: null, session: null })
export function UserProvider({ children }) {
const [session, setSession] = useState(null)
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => setSession(session))
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, session) => setSession(session))
return () => subscription.unsubscribe()
}, [])
return (
<UserContext.Provider value={{ user: session?.user ?? null, session }}>
{children}
</UserContext.Provider>
)
}
export const useUser = () => useContext(UserContext)
@@ -10,7 +10,6 @@
"format": "prettier --cache --write \"pages/**/*.{js,jsx,ts,tsx,css,md,json}\""
},
"dependencies": {
"@supabase/auth-ui-react": "^0.4.7",
"@supabase/ssr": "latest",
"@supabase/supabase-js": "^2",
"next": "^13.1.6",
@@ -1,20 +1,89 @@
import { useSupabaseClient, useUser } from '@/lib/supabase-context'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
import type { NextPage } from 'next'
import { FormEvent, useState } from 'react'
import styles from '../styles/Home.module.css'
import { useSupabaseClient, useUser } from '@/lib/supabase-context'
type Mode = 'sign-in' | 'sign-up'
const LoginPage: NextPage = () => {
const supabaseClient = useSupabaseClient()
const user = useUser()
const [mode, setMode] = useState<Mode>('sign-in')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [message, setMessage] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
setLoading(true)
setMessage(null)
setError(null)
if (mode === 'sign-in') {
const { error } = await supabaseClient.auth.signInWithPassword({ email, password })
if (error) setError(error.message)
} else {
const { data, error } = await supabaseClient.auth.signUp({
email,
password,
options: { emailRedirectTo: `${window.location.origin}/` },
})
if (error) {
setError(error.message)
} else if (data.user && !data.session) {
setMessage('Check your email for a confirmation link to complete sign up.')
}
}
setLoading(false)
}
if (!user) {
return (
<main className={styles.main}>
<Auth
redirectTo="http://localhost:3000/"
appearance={{ theme: ThemeSupa }}
supabaseClient={supabaseClient}
/>
<form onSubmit={handleSubmit} style={{ display: 'grid', gap: 12, minWidth: 280 }}>
<h1>{mode === 'sign-in' ? 'Sign in' : 'Sign up'}</h1>
<label style={{ display: 'grid', gap: 4 }}>
Email
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="email"
/>
</label>
<label style={{ display: 'grid', gap: 4 }}>
Password
<input
type="password"
required
minLength={6}
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete={mode === 'sign-in' ? 'current-password' : 'new-password'}
/>
</label>
<button type="submit" disabled={loading}>
{loading ? 'Loading...' : mode === 'sign-in' ? 'Sign in' : 'Sign up'}
</button>
<button
type="button"
onClick={() => {
setMode(mode === 'sign-in' ? 'sign-up' : 'sign-in')
setError(null)
setMessage(null)
}}
>
{mode === 'sign-in' ? 'Need an account? Sign up' : 'Already have an account? Sign in'}
</button>
{error && <p style={{ color: 'crimson' }}>{error}</p>}
{message && <p>{message}</p>}
</form>
</main>
)
}
@@ -9,8 +9,6 @@
"start": "next start"
},
"dependencies": {
"@supabase/auth-ui-react": "^0.4.7",
"@supabase/auth-ui-shared": "^0.1.8",
"@supabase/supabase-js": "^2",
"jwt-decode": "^4.0.0",
"next": "latest",
@@ -1,10 +1,14 @@
import { useState, useEffect } from 'react'
import { supabase } from 'lib/Store'
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
import { useEffect, useState } from 'react'
const Home = () => {
const [session, setSession] = useState(null)
const [mode, setMode] = useState('sign-in')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState(null)
const [message, setMessage] = useState(null)
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
@@ -20,16 +24,113 @@ const Home = () => {
return () => subscription.unsubscribe()
}, [])
const handleSubmit = async (event) => {
event.preventDefault()
setIsLoading(true)
setError(null)
setMessage(null)
if (mode === 'sign-in') {
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) setError(error.message)
} else {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: { emailRedirectTo: `${window.location.origin}/` },
})
if (error) {
setError(error.message)
} else if (data.user && !data.session) {
setMessage('Check your email for a confirmation link to complete sign up.')
}
}
setIsLoading(false)
}
const handleGitHubSignIn = async () => {
setIsLoading(true)
setError(null)
const { error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: { redirectTo: `${window.location.origin}/` },
})
if (error) setError(error.message)
setIsLoading(false)
}
if (!session) {
return (
<div className="w-full h-full flex justify-center items-center p-4 bg-gray-300">
<div className="w-full sm:w-1/2 xl:w-1/3">
<div className="border-teal p-8 border-t-12 bg-white mb-6 rounded-lg shadow-lg bg-white">
<Auth
supabaseClient={supabase}
appearance={{ theme: ThemeSupa }}
providers={['github']}
/>
<div className="border-teal p-8 border-t-12 bg-white mb-6 rounded-lg shadow-lg">
<h1 className="text-2xl font-bold mb-4">
{mode === 'sign-in' ? 'Sign in' : 'Sign up'}
</h1>
<form onSubmit={handleSubmit} className="flex flex-col gap-3">
<label className="flex flex-col gap-1 text-sm">
Email
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="email"
className="border rounded px-2 py-1"
/>
</label>
<label className="flex flex-col gap-1 text-sm">
Password
<input
type="password"
required
minLength={6}
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete={mode === 'sign-in' ? 'current-password' : 'new-password'}
className="border rounded px-2 py-1"
/>
</label>
<button
type="submit"
disabled={isLoading}
className="bg-blue-600 text-white rounded px-3 py-2 disabled:opacity-50"
>
{isLoading ? 'Loading...' : mode === 'sign-in' ? 'Sign in' : 'Sign up'}
</button>
</form>
<div className="flex items-center gap-2 my-4">
<div className="flex-1 h-px bg-gray-200" />
<span className="text-xs text-gray-500">or</span>
<div className="flex-1 h-px bg-gray-200" />
</div>
<button
type="button"
onClick={handleGitHubSignIn}
disabled={isLoading}
className="w-full bg-gray-900 text-white rounded px-3 py-2"
>
{isLoading ? 'Loading...' : 'Continue with GitHub'}
</button>
<button
type="button"
onClick={() => {
setMode(mode === 'sign-in' ? 'sign-up' : 'sign-in')
setError(null)
setMessage(null)
}}
className="mt-4 text-sm text-blue-600 underline"
>
{mode === 'sign-in' ? 'Need an account? Sign up' : 'Already have an account? Sign in'}
</button>
{error && <p className="mt-3 text-sm text-red-600">{error}</p>}
{message && <p className="mt-3 text-sm text-gray-700">{message}</p>}
</div>
</div>
</div>
@@ -12,7 +12,6 @@
},
"dependencies": {
"@next/font": "13.1.6",
"@supabase/auth-ui-react": "^0.4.7",
"@supabase/ssr": "latest",
"@supabase/supabase-js": "^2",
"@types/node": "18.14.0",
@@ -1,12 +1,20 @@
import Head from 'next/head'
import { supabase } from '@/lib/initSupabase'
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
import TodoList from '@/components/TodoList'
import { Session } from '@supabase/supabase-js'
import { useEffect, useState } from 'react'
import Head from 'next/head'
import { FormEvent, useEffect, useState } from 'react'
import TodoList from '@/components/TodoList'
import { supabase } from '@/lib/initSupabase'
type Mode = 'sign-in' | 'sign-up'
export default function Home() {
const [session, setSession] = useState<Session | null>(null)
const [mode, setMode] = useState<Mode>('sign-in')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [message, setMessage] = useState<string | null>(null)
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => setSession(session))
@@ -18,6 +26,31 @@ export default function Home() {
return () => subscription.unsubscribe()
}, [])
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
setLoading(true)
setError(null)
setMessage(null)
if (mode === 'sign-in') {
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) setError(error.message)
} else {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: { emailRedirectTo: `${window.location.origin}/` },
})
if (error) {
setError(error.message)
} else if (data.user && !data.session) {
setMessage('Check your email for a confirmation link to complete sign up.')
}
}
setLoading(false)
}
return (
<>
<Head>
@@ -32,9 +65,55 @@ export default function Home() {
<div className="w-full h-full flex justify-center items-center p-4">
<div className="w-full h-full sm:h-auto sm:w-2/5 max-w-sm p-5 bg-white shadow flex flex-col text-base">
<span className="font-sans text-4xl text-center pb-2 mb-1 border-b mx-4 align-center">
Login
{mode === 'sign-in' ? 'Sign In' : 'Sign up'}
</span>
<Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} theme="dark" />
<form onSubmit={handleSubmit} className="flex flex-col gap-3 mt-4">
<label className="flex flex-col gap-1 text-sm">
Email
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="email"
className="border rounded px-2 py-1"
/>
</label>
<label className="flex flex-col gap-1 text-sm">
Password
<input
type="password"
required
minLength={6}
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete={mode === 'sign-in' ? 'current-password' : 'new-password'}
className="border rounded px-2 py-1"
/>
</label>
<button
type="submit"
disabled={loading}
className="btn-black w-full disabled:opacity-50"
>
{loading ? 'Loading...' : mode === 'sign-in' ? 'Sign in' : 'Sign up'}
</button>
</form>
<button
type="button"
onClick={() => {
setMode(mode === 'sign-in' ? 'sign-up' : 'sign-in')
setError(null)
setMessage(null)
}}
className="mt-3 text-sm text-blue-600 underline"
>
{mode === 'sign-in'
? 'Need an account? Sign up'
: 'Already have an account? Sign in'}
</button>
{error && <p className="mt-3 text-sm text-red-600">{error}</p>}
{message && <p className="mt-3 text-sm text-gray-700">{message}</p>}
</div>
</div>
</div>
@@ -1,25 +1,36 @@
# Supabase Next.js Auth & User Management Starter
This example will set you up for a very common situation: users can sign up or sign in and then update their account with public profile information, including a profile image.
This example sets you up for a very common situation: users can sign up or sign in and then update their account with public profile information, including a profile image.
This demonstrates how to use:
- User signups using Supabase [Auth](https://supabase.com/auth).
- Supabase [SSR Auth for Next.js](https://supabase.com/docs/guides/auth/server-side/nextjs).
- Supabase [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-ui).
- User avatar images using Supabase [Storage](https://supabase.com/storage)
- Public profiles restricted with [Policies](https://supabase.com/docs/guides/auth#policies).
- Frontend using [Next.js](<[nextjs.org/](https://nextjs.org/)>).
- Supabase [SSR Auth for Next.js](https://supabase.com/docs/guides/auth/server-side/nextjs) with the Next.js App Router and Server Actions.
- User avatar images using Supabase [Storage](https://supabase.com/storage).
- Public profiles restricted with [Row Level Security policies](https://supabase.com/docs/guides/auth/row-level-security).
- Frontend using [Next.js](https://nextjs.org/) (App Router) with React 19 and Tailwind CSS v4.
## Technologies used
- Frontend:
- [Next.js](https://github.com/vercel/next.js) - a React framework for production.
- [Supabase.js](https://supabase.com/docs/library/getting-started) for user management and realtime data syncing.
- Supabase [`@supabase/ssr`](https://supabase.com/docs/guides/auth/server-side/nextjs) for SSR auth.
- Supabase [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-ui).
- [Next.js](https://github.com/vercel/next.js) (App Router) — a React framework for production.
- [`@supabase/ssr`](https://supabase.com/docs/guides/auth/server-side/nextjs) for cookie-based SSR auth, used from both Server Components and Server Actions.
- [`@supabase/supabase-js`](https://supabase.com/docs/library/getting-started) for the browser client and realtime data.
- [Tailwind CSS v4](https://tailwindcss.com/) for styling.
- Backend:
- [supabase.com/dashboard](https://supabase.com/dashboard/): hosted Postgres database with restful API for usage with Supabase.js.
- [supabase.com/dashboard](https://supabase.com/dashboard/) — hosted Postgres database with a REST API, Auth, and Storage.
- Local development via the [Supabase CLI](https://supabase.com/docs/guides/cli).
## Project structure
- `app/login/` — login and signup form. The form posts to Server Actions in `app/login/actions.ts` that call `supabase.auth.signInWithPassword()` and `supabase.auth.signUp()`.
- `app/account/` — protected profile page. Uses a Supabase server client to check the session and renders an account form with avatar upload.
- `app/auth/confirm/route.ts` — handles the email confirmation callback by verifying the OTP token and redirecting.
- `app/auth/signout/route.ts` — server route that signs the user out.
- `lib/supabase/client.ts` — browser client (`createBrowserClient`).
- `lib/supabase/server.ts` — server client (`createServerClient`) wired up to Next.js cookies.
- `supabase/migrations/` — database schema for the `profiles` table, RLS policies, the `handle_new_user` trigger, and the `avatars` storage bucket.
- `supabase/config.toml` — local Supabase configuration used by `npx supabase start`.
## Instant deploy
@@ -27,43 +38,79 @@ The Vercel deployment will guide you through creating a Supabase account and pro
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fuser-management%2Fnextjs-user-management&project-name=supabase-nextjs-user-management&repository-name=supabase-nextjs-user-management&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6&external-id=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fuser-management%2Fnextjs-user-management)
### 1. Create new project
## Run locally
Sign up to Supabase - [https://supabase.com/dashboard](https://supabase.com/dashboard) and create a new project. Wait for your database to start.
### 1. Install dependencies
### 2. Run "User Management" Quickstart
Requires Node.js 20+ and `npx` (bundled with npm).
Once your database has started, head over to your project's `SQL Editor` and run the "User Management Starter" quickstart. On the `SQL editor` page, scroll down until you see `User Management Starter: Sets up a public Profiles table which you can access with your API`. Click that, then click `RUN` to execute that query and create a new `profiles` table. When that's finished, head over to the `Table Editor` and see your new `profiles` table.
```bash
npm install
```
### 3. Get the URL and Key
### 2. Start a local Supabase stack
Go to the Project Settings (the cog icon), open the API tab, and find your API URL and `anon` key, you'll need these in the next step.
The example includes a `supabase/` directory with the schema and config needed to run a local stack via the Supabase CLI.
The `anon` key is your client-side API key. It allows "anonymous access" to your database, until the user has logged in. Once they have logged in, the keys will switch to the user's own login token. This enables row level security for your data. Read more about this [below](#postgres-row-level-security).
```bash
npx supabase start
```
![image](https://user-images.githubusercontent.com/10214025/88916245-528c2680-d298-11ea-8a71-708f93e1ce4f.png)
This boots Postgres, Auth, Storage, and Supabase Studio locally and runs the migrations in `supabase/migrations/`. When it finishes, it prints your local API URL and keys.
**_NOTE_**: The `secret` key has full access to your data, bypassing any security policies. These keys have to be kept secret and are meant to be used in server environments and never on a client or browser.
### 3. Configure environment variables
## Supabase details
Copy the development env template:
### Using a Remote Supabase Project
```bash
cp .env.example .env.local
```
1. Create or select a project on [Supabase Dashboard](https://supabase.com/dashboard).
2. Copy and fill the dotenv template `cp .env.production.example .env.production`
3. Link the remote project to your local environment:
The defaults in `.env.example` already match the local stack (API URL `http://127.0.0.1:54321` and the demo publishable key). Update them if your local ports differ.
### 4. Start the dev server
```bash
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) to use the app.
## Using a remote Supabase project
### 1. Create a project
Sign up at [https://supabase.com/dashboard](https://supabase.com/dashboard) and create a new project. Wait for your database to start.
### 2. Get the URL and publishable key
Go to the Project Settings (the cog icon), open the API tab, and find your **Project URL** and **publishable key**.
The `publishable` key is your client-side API key. It allows "anonymous access" to your database until the user logs in. Once they log in, the user's own JWT is used, which enables Row Level Security to scope data per user. Read more [below](#postgres-row-level-security).
> **Note:** The `secret` (service role) key has full access to your data and bypasses all security policies. Keep it in server environments only — never expose it to the client or browser.
### 3. Link and push the schema
Copy the production env template and fill it in with your project URL, publishable key, and the URL(s) you want to allow as redirect targets:
```bash
cp .env.production.example .env.production
```
Link your local checkout to the remote project:
```bash
SUPABASE_ENV=production npx supabase@latest link --project-ref <your-project-ref>
```
3. Sync the configuration:
Push the `supabase/config.toml` settings (Auth site URL, redirect URLs, etc.):
```bash
SUPABASE_ENV=production npx supabase@latest config push
```
4. Sync the database schema:
Push the database schema in `supabase/migrations/`:
```bash
SUPABASE_ENV=production npx supabase@latest db push
@@ -81,19 +128,19 @@ Supabase integrates seamlessly with Vercel's preview branches, giving each branc
- `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`
3. Create a new branch, make changes (e.g., update `max_frequency`), and push the branch to Git.
- Open a pull request to trigger Vercel + Supabase integration.
- Open a pull request to trigger the Vercel + Supabase integration.
- Upon successful deployment, the preview environment reflects the changes.
![Preview Checks](https://github.com/user-attachments/assets/db688cc2-60fd-4463-bbed-e8ecc11b1a39)
### Postgres Row level security
## Postgres Row Level Security
This project uses very high-level Authorization using Postgres' Row Level Security.
When you start a Postgres database on Supabase, we populate it with an `auth` schema, and some helper functions.
This project uses high-level authorization via Postgres' Row Level Security.
When you start a Postgres database on Supabase, we populate it with an `auth` schema and some helper functions.
When a user logs in, they are issued a JWT with the role `authenticated` and their UUID.
We can use these details to provide fine-grained control over what each user can and cannot do.
This is a trimmed-down schema, with the policies:
The schema and policies that this example uses (see `supabase/migrations/20221017024722_init.sql`):
```sql
-- Create a table for public profiles
@@ -151,21 +198,21 @@ create policy "Anyone can update their own avatar." on storage.objects
for update using ( auth.uid() = owner ) with check (bucket_id = 'avatars');
```
## More Supabase Examples & Resources
## More Supabase examples & resources
## Examples
### Examples
These official examples are maintained by the Supabase team:
- [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments)
- [Next.js Slack Clone](https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone)
- [Next.js 13 Data Fetching](https://github.com/supabase/supabase/tree/master/examples/caching/with-nextjs-13)
- [Next.js Data Fetching](https://github.com/supabase/supabase/tree/master/examples/caching/with-nextjs-13)
- [And more...](https://github.com/supabase/supabase/tree/master/examples)
## Other resources
### Other resources
- [[Docs] Next.js User Management Quickstart](https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs)
- [[Egghead.io] Build a SaaS product with Next.js, Supabase and Stripe](https://egghead.io/courses/build-a-saas-product-with-next-js-supabase-and-stripe-61f2bc20)
- [[Docs] Server-Side Auth for Next.js](https://supabase.com/docs/guides/auth/server-side/nextjs)
- [[Blog] Fetching and caching Supabase data in Next.js 13 Server Components](https://supabase.com/blog/fetching-and-caching-supabase-data-in-next-js-server-components)
## Authors