* fix: rewrite relative URLs when syncing to GitHub discussion Relative URLs back to supabse.com won't work in GitHub discussions, so rewrite them back to absolute URLs starting with https://supabase.com * fix: replace all supabase urls with relative urls * chore: add linting for relative urls * chore: bump linter version * Prettier --------- Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>
1334 lines
39 KiB
Plaintext
1334 lines
39 KiB
Plaintext
---
|
|
id: 'nextjs'
|
|
title: 'Supabase Auth with the Next.js App Router'
|
|
description: 'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.'
|
|
sidebar_label: 'Next.js'
|
|
sitemapPriority: 0.5
|
|
---
|
|
|
|
<Admonition type="caution">
|
|
|
|
The `auth-helpers` are now deprecated. Use `@supabase/ssr` to set up Auth for your Next.js app. See the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs) to learn how.
|
|
|
|
</Admonition>
|
|
|
|
<Accordion
|
|
type="default"
|
|
openBehaviour="multiple"
|
|
chevronAlign="right"
|
|
justified
|
|
size="medium"
|
|
className="text-foreground-light border-b mt-8 pb-2"
|
|
>
|
|
|
|
<AccordionItem
|
|
header="See legacy docs"
|
|
id="legacy-docs"
|
|
>
|
|
|
|
The [Next.js Auth Helpers package](https://github.com/supabase/auth-helpers) configures Supabase Auth to store the user's `session` in a `cookie`, rather than `localStorage`. This makes it available across the client and server of the App Router - [Client Components](/docs/guides/auth/auth-helpers/nextjs#client-components), [Server Components](/docs/guides/auth/auth-helpers/nextjs#server-components), [Server Actions](/docs/guides/auth/auth-helpers/nextjs#server-actions), [Route Handlers](/docs/guides/auth/auth-helpers/nextjs#route-handlers) and [Middleware](/docs/guides/auth/auth-helpers/nextjs#middleware). The `session` is automatically sent along with any requests to Supabase.
|
|
|
|
<div className="video-container">
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/w3LD0Z73vgU"
|
|
frameBorder="1"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
></iframe>
|
|
</div>
|
|
|
|
<Admonition type="note">
|
|
|
|
If you are using the `pages` directory, check out [Auth Helpers in Next.js Pages Directory](/docs/guides/auth/auth-helpers/nextjs-pages).
|
|
|
|
</Admonition>
|
|
|
|
## Install Next.js Auth helpers library
|
|
|
|
```sh Terminal
|
|
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
|
|
```
|
|
|
|
## Declare environment variables
|
|
|
|
Retrieve your project's URL and anon key from your [API settings](/dashboard/project/_/settings/api), and create a `.env.local` file with the following environment variables:
|
|
|
|
```bash .env.local
|
|
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
|
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your-supabase-publishable-key
|
|
```
|
|
|
|
## Managing session with middleware
|
|
|
|
When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.
|
|
|
|
Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.
|
|
|
|
Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. To avoid unnecessary execution, we include a matcher config to decide when the middleware should run. You can read more on matching paths in the Next.js [documentation](https://nextjs.org/docs/app/building-your-application/routing/middleware#matching-paths). We'll use Middleware to refresh the user's session before loading Server Component routes.
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
Create a new `middleware.js` file in the root of your project and populate with the following:
|
|
|
|
```js middleware.js
|
|
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
export async function middleware(req) {
|
|
const res = NextResponse.next()
|
|
|
|
// Create a Supabase client configured to use cookies
|
|
const supabase = createMiddlewareClient({ req, res })
|
|
|
|
// Refresh session if expired - required for Server Components
|
|
await supabase.auth.getUser()
|
|
|
|
return res
|
|
}
|
|
|
|
// Ensure the middleware is only called for relevant paths.
|
|
export const config = {
|
|
matcher: [
|
|
/*
|
|
* Match all request paths except for the ones starting with:
|
|
* - _next/static (static files)
|
|
* - _next/image (image optimization files)
|
|
* - favicon.ico (favicon file)
|
|
* Feel free to modify this pattern to include more paths.
|
|
*/
|
|
'/((?!_next/static|_next/image|favicon.ico).*)',
|
|
],
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
Create a new `middleware.ts` file in the root of your project and populate with the following:
|
|
|
|
```ts middleware.ts
|
|
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import type { NextRequest } from 'next/server'
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function middleware(req: NextRequest) {
|
|
const res = NextResponse.next()
|
|
|
|
// Create a Supabase client configured to use cookies
|
|
const supabase = createMiddlewareClient<Database>({ req, res })
|
|
|
|
// Refresh session if expired - required for Server Components
|
|
await supabase.auth.getSession()
|
|
|
|
return res
|
|
}
|
|
|
|
// Ensure the middleware is only called for relevant paths.
|
|
export const config = {
|
|
matcher: [
|
|
/*
|
|
* Match all request paths except for the ones starting with:
|
|
* - _next/static (static files)
|
|
* - _next/image (image optimization files)
|
|
* - favicon.ico (favicon file)
|
|
*/
|
|
'/((?!_next/static|_next/image|favicon.ico).*)',
|
|
],
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createMiddlewareClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
<Admonition type="note">
|
|
|
|
The `getSession` function must be called for any Server Component routes that use a Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
## Managing sign-in with Code Exchange
|
|
|
|
The Next.js Auth Helpers are configured to use the [server-side auth flow](/docs/guides/auth/server-side-rendering) to sign users into your application. This requires you to setup a `Code Exchange` route, to exchange an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
|
|
|
|
To make this work with Next.js, we create a callback Route Handler that performs this exchange:
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
Create a new file at `app/auth/callback/route.js` and populate with the following:
|
|
|
|
```js app/auth/callback/route.js
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
export async function GET(request) {
|
|
const requestUrl = new URL(request.url)
|
|
const code = requestUrl.searchParams.get('code')
|
|
|
|
if (code) {
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
|
await supabase.auth.exchangeCodeForSession(code)
|
|
}
|
|
|
|
// URL to redirect to after sign in process completes
|
|
return NextResponse.redirect(requestUrl.origin)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
Create a new file at `app/auth/callback/route.ts` and populate with the following:
|
|
|
|
```ts app/auth/callback/route.ts
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import type { NextRequest } from 'next/server'
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const requestUrl = new URL(request.url)
|
|
const code = requestUrl.searchParams.get('code')
|
|
|
|
if (code) {
|
|
const cookieStore = await cookies()
|
|
const supabase = createRouteHandlerClient<Database>({ cookies: () => cookieStore })
|
|
await supabase.auth.exchangeCodeForSession(code)
|
|
}
|
|
|
|
// URL to redirect to after sign in process completes
|
|
return NextResponse.redirect(requestUrl.origin)
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
## Authentication
|
|
|
|
<div className="video-container">
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/-7K6DRWfEGM"
|
|
frameBorder="1"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
></iframe>
|
|
</div>
|
|
|
|
Authentication can be initiated [client](/docs/guides/auth/auth-helpers/nextjs#client-side) or [server-side](/docs/guides/auth/auth-helpers/nextjs#server-side). All of the [supabase-js authentication strategies](/docs/reference/javascript/auth-api) are supported with the Auth Helpers client.
|
|
|
|
<Admonition type="note">
|
|
|
|
The authentication flow requires the [Code Exchange Route](/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange) to exchange a `code` for the user's `session`.
|
|
|
|
</Admonition>
|
|
|
|
### Client-side
|
|
|
|
Client Components can be used to trigger the authentication process from event handlers.
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/login/page.jsx
|
|
'use client'
|
|
|
|
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
import { useRouter } from 'next/navigation'
|
|
import { useState } from 'react'
|
|
|
|
export default function Login() {
|
|
const [email, setEmail] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const router = useRouter()
|
|
const supabase = createClientComponentClient()
|
|
|
|
const handleSignUp = async () => {
|
|
await supabase.auth.signUp({
|
|
email,
|
|
password,
|
|
options: {
|
|
emailRedirectTo: `${location.origin}/auth/callback`,
|
|
},
|
|
})
|
|
router.refresh()
|
|
}
|
|
|
|
const handleSignIn = async () => {
|
|
await supabase.auth.signInWithPassword({
|
|
email,
|
|
password,
|
|
})
|
|
router.refresh()
|
|
}
|
|
|
|
const handleSignOut = async () => {
|
|
await supabase.auth.signOut()
|
|
router.refresh()
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<input name="email" onChange={(e) => setEmail(e.target.value)} value={email} />
|
|
<input
|
|
type="password"
|
|
name="password"
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
value={password}
|
|
/>
|
|
<button onClick={handleSignUp}>Sign up</button>
|
|
<button onClick={handleSignIn}>Sign in</button>
|
|
<button onClick={handleSignOut}>Sign out</button>
|
|
</>
|
|
)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/login/page.tsx
|
|
'use client'
|
|
|
|
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
import { useRouter } from 'next/navigation'
|
|
import { useState } from 'react'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export default function Login() {
|
|
const [email, setEmail] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const router = useRouter()
|
|
const supabase = createClientComponentClient<Database>()
|
|
|
|
const handleSignUp = async () => {
|
|
await supabase.auth.signUp({
|
|
email,
|
|
password,
|
|
options: {
|
|
emailRedirectTo: `${location.origin}/auth/callback`,
|
|
},
|
|
})
|
|
router.refresh()
|
|
}
|
|
|
|
const handleSignIn = async () => {
|
|
await supabase.auth.signInWithPassword({
|
|
email,
|
|
password,
|
|
})
|
|
router.refresh()
|
|
}
|
|
|
|
const handleSignOut = async () => {
|
|
await supabase.auth.signOut()
|
|
router.refresh()
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<input name="email" onChange={(e) => setEmail(e.target.value)} value={email} />
|
|
<input
|
|
type="password"
|
|
name="password"
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
value={password}
|
|
/>
|
|
<button onClick={handleSignUp}>Sign up</button>
|
|
<button onClick={handleSignIn}>Sign in</button>
|
|
<button onClick={handleSignOut}>Sign out</button>
|
|
</>
|
|
)
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createClientComponentClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
### Server-side
|
|
|
|
The combination of [Server Components](https://nextjs.org/docs/getting-started/react-essentials#server-components) and [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) can be used to trigger the authentication process from form submissions.
|
|
|
|
#### Sign up route
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/auth/sign-up/route.js
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
export async function POST(request) {
|
|
const requestUrl = new URL(request.url)
|
|
const formData = await request.formData()
|
|
const email = formData.get('email')
|
|
const password = formData.get('password')
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
|
|
|
await supabase.auth.signUp({
|
|
email,
|
|
password,
|
|
options: {
|
|
emailRedirectTo: `${requestUrl.origin}/auth/callback`,
|
|
},
|
|
})
|
|
|
|
return NextResponse.redirect(requestUrl.origin, {
|
|
status: 301,
|
|
})
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/auth/sign-up/route.ts
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function POST(request: Request) {
|
|
const requestUrl = new URL(request.url)
|
|
const formData = await request.formData()
|
|
const email = String(formData.get('email'))
|
|
const password = String(formData.get('password'))
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient<Database>({ cookies: () => cookieStore })
|
|
|
|
await supabase.auth.signUp({
|
|
email,
|
|
password,
|
|
options: {
|
|
emailRedirectTo: `${requestUrl.origin}/auth/callback`,
|
|
},
|
|
})
|
|
|
|
return NextResponse.redirect(requestUrl.origin, {
|
|
status: 301,
|
|
})
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
<Admonition type="note">
|
|
|
|
Returning a `301` status redirects from a POST to a GET route
|
|
|
|
</Admonition>
|
|
|
|
#### Login route
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/auth/login/route.js
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
export async function POST(request) {
|
|
const requestUrl = new URL(request.url)
|
|
const formData = await request.formData()
|
|
const email = formData.get('email')
|
|
const password = formData.get('password')
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
|
|
|
await supabase.auth.signInWithPassword({
|
|
email,
|
|
password,
|
|
})
|
|
|
|
return NextResponse.redirect(requestUrl.origin, {
|
|
status: 301,
|
|
})
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/auth/login/route.ts
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function POST(request: Request) {
|
|
const requestUrl = new URL(request.url)
|
|
const formData = await request.formData()
|
|
const email = String(formData.get('email'))
|
|
const password = String(formData.get('password'))
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient<Database>({ cookies: () => cookieStore })
|
|
|
|
await supabase.auth.signInWithPassword({
|
|
email,
|
|
password,
|
|
})
|
|
|
|
return NextResponse.redirect(requestUrl.origin, {
|
|
status: 301,
|
|
})
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
<Admonition type="note">
|
|
|
|
Returning a `301` status redirects from a POST to a GET route
|
|
|
|
</Admonition>
|
|
|
|
#### Logout route
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/auth/logout/route.js
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
export async function POST(request) {
|
|
const requestUrl = new URL(request.url)
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
|
|
|
await supabase.auth.signOut()
|
|
|
|
return NextResponse.redirect(`${requestUrl.origin}/login`, {
|
|
status: 301,
|
|
})
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/auth/logout/route.ts
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { cookies } from 'next/headers'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function POST(request: Request) {
|
|
const requestUrl = new URL(request.url)
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient<Database>({ cookies: () => cookieStore })
|
|
|
|
await supabase.auth.signOut()
|
|
|
|
return NextResponse.redirect(`${requestUrl.origin}/login`, {
|
|
status: 301,
|
|
})
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
#### Login page
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/login/page.jsx
|
|
export default function Login() {
|
|
return (
|
|
<form action="/auth/login" method="post">
|
|
<label htmlFor="email">Email</label>
|
|
<input name="email" />
|
|
<label htmlFor="password">Password</label>
|
|
<input type="password" name="password" />
|
|
<button>Sign In</button>
|
|
<button formAction="/auth/sign-up">Sign Up</button>
|
|
<button formAction="/auth/logout">Sign Out</button>
|
|
</form>
|
|
)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/login/page.tsx
|
|
export default function Login() {
|
|
return (
|
|
<form action="/auth/login" method="post">
|
|
<label htmlFor="email">Email</label>
|
|
<input name="email" />
|
|
<label htmlFor="password">Password</label>
|
|
<input type="password" name="password" />
|
|
<button>Sign In</button>
|
|
<button formAction="/auth/sign-up">Sign Up</button>
|
|
</form>
|
|
)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
## Creating a Supabase client
|
|
|
|
There are 5 ways to access the Supabase client with the Next.js Auth Helpers:
|
|
|
|
- [Client Components](/docs/guides/auth/auth-helpers/nextjs#client-components) — `createClientComponentClient` in Client Components
|
|
- [Server Components](/docs/guides/auth/auth-helpers/nextjs#server-components) — `createServerComponentClient` in Server Components
|
|
- [Server Actions](/docs/guides/auth/auth-helpers/nextjs#server-actions) — `createServerActionClient` in Server Actions
|
|
- [Route Handlers](/docs/guides/auth/auth-helpers/nextjs#route-handlers) — `createRouteHandlerClient` in Route Handlers
|
|
- [Middleware](/docs/guides/auth/auth-helpers/nextjs#middleware) — `createMiddlewareClient` in Middleware
|
|
|
|
This allows for the Supabase client to be instantiated in the correct context. All you need to change is the context in the middle `create[ClientComponent|ServerComponent|ServerAction|RouteHandler|Middleware]Client` and the Auth Helpers will take care of the rest.
|
|
|
|
### Client components
|
|
|
|
<div className="video-container">
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/6Sb8R1PYhTY"
|
|
frameBorder="1"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
></iframe>
|
|
</div>
|
|
|
|
[Client Components](https://nextjs.org/docs/getting-started/react-essentials#client-components) allow the use of client-side hooks - such as `useEffect` and `useState`. They can be used to request data from Supabase client-side, and [subscribe to realtime events](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx).
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/client/page.jsx
|
|
'use client'
|
|
|
|
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
import { useEffect, useState } from 'react'
|
|
|
|
export default function Page() {
|
|
const [todos, setTodos] = useState()
|
|
const supabase = createClientComponentClient()
|
|
|
|
useEffect(() => {
|
|
const getData = async () => {
|
|
const { data } = await supabase.from('todos').select()
|
|
setTodos(data)
|
|
}
|
|
|
|
getData()
|
|
}, [])
|
|
|
|
return todos ? <pre>{JSON.stringify(todos, null, 2)}</pre> : <p>Loading todos...</p>
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/client/page.tsx
|
|
'use client'
|
|
|
|
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
import { useEffect, useState } from 'react'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
type Todo = Database['public']['Tables']['todos']['Row']
|
|
|
|
export default function Page() {
|
|
const [todos, setTodos] = useState<Todo[] | null>(null)
|
|
const supabase = createClientComponentClient<Database>()
|
|
|
|
useEffect(() => {
|
|
const getData = async () => {
|
|
const { data } = await supabase.from('todos').select()
|
|
setTodos(data)
|
|
}
|
|
|
|
getData()
|
|
}, [])
|
|
|
|
return todos ? <pre>{JSON.stringify(todos, null, 2)}</pre> : <p>Loading todos...</p>
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createClientComponentClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
<Admonition type="note">
|
|
|
|
Check out the [Next.js auth example repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) for more examples, including [realtime subscriptions](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx).
|
|
|
|
</Admonition>
|
|
|
|
#### Singleton
|
|
|
|
The `createClientComponentClient` function implements a [Singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) by default, meaning that all invocations will return the same Supabase client instance. If you need multiple Supabase instances across Client Components, you can pass an additional configuration option `{ isSingleton: false }` to get a new client every time this function is called.
|
|
|
|
```jsx
|
|
const supabase = createClientComponentClient({ isSingleton: false })
|
|
```
|
|
|
|
### Server components
|
|
|
|
<div className="video-container">
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/ywvXGW6P4Gs"
|
|
frameBorder="1"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
></iframe>
|
|
</div>
|
|
|
|
[Server Components](https://nextjs.org/docs/getting-started/react-essentials#server-components) allow for asynchronous data to be fetched server-side.
|
|
|
|
<Admonition type="note">
|
|
|
|
In order to use Supabase in Server Components, you need to have implemented the [Middleware](/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware) steps above.
|
|
|
|
</Admonition>
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/page.jsx
|
|
import { cookies } from 'next/headers'
|
|
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
|
|
export default async function Page() {
|
|
const cookieStore = cookies()
|
|
const supabase = createServerComponentClient({ cookies: () => cookieStore })
|
|
const { data } = await supabase.from('todos').select()
|
|
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/page.tsx
|
|
import { cookies } from 'next/headers'
|
|
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export default async function ServerComponent() {
|
|
const cookieStore = cookies()
|
|
const supabase = createServerComponentClient<Database>({ cookies: () => cookieStore })
|
|
const { data } = await supabase.from('todos').select()
|
|
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createServerComponentClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
<Admonition type="note">
|
|
|
|
Check out the [Next.js auth example repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) for more examples, including redirecting unauthenticated users - [protected pages](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/[id]/page.tsx).
|
|
|
|
</Admonition>
|
|
|
|
### Server actions
|
|
|
|
<div className="video-container">
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/4_epZIxqCho"
|
|
frameBorder="1"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
></iframe>
|
|
</div>
|
|
|
|
[Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions) allow mutations to be performed server-side.
|
|
|
|
<Admonition type="note">
|
|
|
|
Next.js Server Actions are currently in `alpha` so may change without notice.
|
|
|
|
</Admonition>
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/new-post/page.jsx
|
|
import { cookies } from 'next/headers'
|
|
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
|
|
import { revalidatePath } from 'next/cache'
|
|
|
|
export default async function NewTodo() {
|
|
const addTodo = async (formData) => {
|
|
'use server'
|
|
|
|
const title = formData.get('title')
|
|
const supabase = createServerActionClient({ cookies })
|
|
await supabase.from('todos').insert({ title })
|
|
revalidatePath('/')
|
|
}
|
|
|
|
return (
|
|
<form action={addTodo}>
|
|
<input name="title" />
|
|
</form>
|
|
)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/new-post/page.tsx
|
|
import { cookies } from 'next/headers'
|
|
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
|
|
import { revalidatePath } from 'next/cache'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export default async function NewTodo() {
|
|
const addTodo = async (formData: FormData) => {
|
|
'use server'
|
|
|
|
const title = formData.get('title')
|
|
const cookieStore = cookies()
|
|
const supabase = createServerActionClient<Database>({ cookies: () => cookieStore })
|
|
await supabase.from('todos').insert({ title })
|
|
revalidatePath('/')
|
|
}
|
|
|
|
return (
|
|
<form action={addTodo}>
|
|
<input name="title" />
|
|
</form>
|
|
)
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createServerActionClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
### Route handlers
|
|
|
|
<div className="video-container">
|
|
<iframe
|
|
src="https://www.youtube-nocookie.com/embed/r6q7ypXbPFI"
|
|
frameBorder="1"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
></iframe>
|
|
</div>
|
|
|
|
[Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) replace API Routes and allow for logic to be performed server-side. They can respond to `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS` requests.
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/api/todos/route.js
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { NextResponse } from 'next/server'
|
|
import { cookies } from 'next/headers'
|
|
|
|
export async function POST(request) {
|
|
const { title } = await request.json()
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
|
const { data } = await supabase.from('todos').insert({ title }).select()
|
|
return NextResponse.json(data)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/api/todos/route.ts
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { NextResponse } from 'next/server'
|
|
import { cookies } from 'next/headers'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function POST(request: Request) {
|
|
const { title } = await request.json()
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient<Database>({ cookies: () => cookieStore })
|
|
const { data } = await supabase.from('todos').insert({ title }).select()
|
|
return NextResponse.json(data)
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
### Middleware
|
|
|
|
See [refreshing session example](/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware) above.
|
|
|
|
### Edge runtime
|
|
|
|
The Next.js Edge Runtime allows you to host Server Components and Route Handlers from Edge nodes, serving the routes as close as possible to your user's location.
|
|
|
|
A route can be configured to use the Edge Runtime by exporting a `runtime` variable set to `edge`. Additionally, the `cookies()` function must be called from the Edge route, before creating a Supabase Client.
|
|
|
|
#### Server components
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/page.jsx
|
|
import { cookies } from 'next/headers'
|
|
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
|
|
export const runtime = 'edge'
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export default async function Page() {
|
|
const cookieStore = cookies()
|
|
|
|
const supabase = createServerComponentClient({
|
|
cookies: () => cookieStore,
|
|
})
|
|
|
|
const { data } = await supabase.from('todos').select()
|
|
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/page.tsx
|
|
import { cookies } from 'next/headers'
|
|
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export const runtime = 'edge'
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export default async function Page() {
|
|
const cookieStore = cookies()
|
|
|
|
const supabase = createServerComponentClient<Database>({
|
|
cookies: () => cookieStore,
|
|
})
|
|
|
|
const { data } = await supabase.from('todos').select()
|
|
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createServerComponentClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
#### Route handlers
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/api/todos/route.js
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { NextResponse } from 'next/server'
|
|
import { cookies } from 'next/headers'
|
|
|
|
export const runtime = 'edge'
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export async function POST(request) {
|
|
const { title } = await request.json()
|
|
const cookieStore = cookies()
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
|
|
|
const { data } = await supabase.from('todos').insert({ title }).select()
|
|
return NextResponse.json(data)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/api/todos/route.ts
|
|
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
|
import { NextResponse } from 'next/server'
|
|
import { cookies } from 'next/headers'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export const runtime = 'edge'
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export async function POST(request: Request) {
|
|
const { title } = await request.json()
|
|
const cookieStore = cookies()
|
|
|
|
const supabase = createRouteHandlerClient<Database>({
|
|
cookies: () => cookieStore,
|
|
})
|
|
|
|
const { data } = await supabase.from('todos').insert({ title }).select()
|
|
return NextResponse.json(data)
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
### Static routes
|
|
|
|
Server Components and Route Handlers are static by default - data is fetched once at build time and the value is cached. Since the request to Supabase now happens at build time, there is no user, session or cookie to pass along with the request to Supabase. Therefore, the `createClient` function from `supabase-js` can be used to fetch data for static routes.
|
|
|
|
#### Server components
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/page.jsx
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
export default async function Page() {
|
|
const supabase = createClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
|
|
)
|
|
|
|
const { data } = await supabase.from('todos').select()
|
|
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/page.tsx
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export default async function Page() {
|
|
const supabase = createClient<Database>(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!
|
|
)
|
|
|
|
const { data } = await supabase.from('todos').select()
|
|
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
#### Route handlers
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="js"
|
|
queryGroup="language"
|
|
>
|
|
<TabPanel id="js" label="JavaScript">
|
|
|
|
```jsx app/api/todos/route.js
|
|
import { createClient } from '@supabase/supabase-js'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
export async function POST(request) {
|
|
const { title } = await request.json()
|
|
|
|
const supabase = createClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
|
|
)
|
|
|
|
const { data } = await supabase.from('todos').insert({ title }).select()
|
|
return NextResponse.json(data)
|
|
}
|
|
```
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel id="ts" label="TypeScript">
|
|
|
|
```tsx app/api/todos/route.ts
|
|
import { createClient } from '@supabase/supabase-js'
|
|
import { NextResponse } from 'next/server'
|
|
|
|
import type { Database } from '@/lib/database.types'
|
|
|
|
export async function POST(request: Request) {
|
|
const { title } = await request.json()
|
|
|
|
const supabase = createClient<Database>(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!
|
|
)
|
|
|
|
const { data } = await supabase.from('todos').insert({ title }).select()
|
|
return NextResponse.json(data)
|
|
}
|
|
```
|
|
|
|
<Admonition type="note">
|
|
|
|
TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createClient` to add type support to the Supabase client.
|
|
|
|
</Admonition>
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
## More examples
|
|
|
|
- [Build a Twitter Clone with the Next.js App Router and Supabase - free egghead course](https://egghead.io/courses/build-a-twitter-clone-with-the-next-js-app-router-and-supabase-19bebadb)
|
|
- [Cookie-based Auth and the Next.js 13 App Router (free course)](https://youtube.com/playlist?list=PL5S4mPUpp4OtMhpnp93EFSo42iQ40XjbF)
|
|
- [Full App Router example](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs)
|
|
- [Realtime Subscriptions](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx)
|
|
- [Protected Routes](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/[id]/page.tsx)
|
|
- [Conditional Rendering in Client Components with SSR](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login-form.tsx)
|
|
|
|
## Migration guide
|
|
|
|
### Migrating to v0.7.X
|
|
|
|
#### PKCE Auth flow
|
|
|
|
PKCE is the new server-side auth flow implemented by the Next.js Auth Helpers. It requires a new Route Handler for `/auth/callback` that exchanges an auth `code` for the user's `session`.
|
|
|
|
Check the [Code Exchange Route steps](/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange) above to implement this Route Handler.
|
|
|
|
#### Authentication
|
|
|
|
For authentication methods that have a `redirectTo` or `emailRedirectTo`, this must be set to this new code exchange Route Handler - `/auth/callback`. This is an example with the `signUp` function:
|
|
|
|
```jsx
|
|
supabase.auth.signUp({
|
|
email: 'valid.email@supabase.io',
|
|
password: 'sup3rs3cur3',
|
|
options: {
|
|
emailRedirectTo: 'http://localhost:3000/auth/callback',
|
|
},
|
|
})
|
|
```
|
|
|
|
#### Deprecated functions
|
|
|
|
With v0.7.x of the Next.js Auth Helpers a new naming convention has been implemented for `createClient` functions. The `createMiddlewareSupabaseClient`, `createBrowserSupabaseClient`, `createServerComponentSupabaseClient` and `createRouteHandlerSupabaseClient` functions have been marked as deprecated, and will be removed in a future version of the Auth Helpers.
|
|
|
|
- `createMiddlewareSupabaseClient` has been replaced with `createMiddlewareClient`
|
|
- `createBrowserSupabaseClient` has been replaced with `createClientComponentClient`
|
|
- `createServerComponentSupabaseClient` has been replaced with `createServerComponentClient`
|
|
- `createRouteHandlerSupabaseClient` has been replaced with `createRouteHandlerClient`
|
|
|
|
#### `createClientComponentClient` returns singleton
|
|
|
|
You no longer need to implement logic to ensure there is only a single instance of the Supabase Client shared across all Client Components - this is now the default and handled by the `createClientComponentClient` function. Call it as many times as you want!
|
|
|
|
```jsx
|
|
"use client";
|
|
|
|
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
|
|
|
|
export default function() {
|
|
const supabase = createClientComponentClient();
|
|
return ...
|
|
}
|
|
```
|
|
|
|
For an example of creating multiple Supabase clients, check [Singleton section](/docs/guides/auth/auth-helpers/nextjs#singleton) above.
|
|
|
|
</AccordionItem>
|
|
|
|
</Accordion>
|