Supabase Auth with the Next.js App Router
The Next.js Auth Helpers package 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, Server Components, Server Actions, Route Handlers and Middleware. The session
is automatically sent along with any requests to Supabase.
Note: If you are using the
pages
directory, check out Auth Helpers in Next.js Pages Directory.
Configuration#
Install Next.js Auth Helpers library#
npm install @supabase/auth-helpers-nextjs
Declare Environment Variables#
Retrieve your project's URL and anon key from your API settings, and create a .env.local
file with the following environment variables:
1NEXT_PUBLIC_SUPABASE_URL=your-supabase-url 2NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
Refresh session with Middleware#
Middleware runs immediately before each route in rendered. Next.js only provides read access to cookies in Server Components, therefore, Middleware is used to refresh the user's session before loading Server Component routes.
Create a new middleware.js
file in the root of your project and populate with the following:
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
export async function middleware(req) {
const res = NextResponse.next()
const supabase = createMiddlewareClient({ req, res })
await supabase.auth.getSession()
return res
}
The
getSession
function must be called for any Server Component routes that use a Supabase client.
Code Exchange Route#
The Code Exchange
route is required for the server-side auth flow implemented by the Next.js Auth Helpers. It exchanges an auth code
for the user's session
, which is set as a cookie for future requests made to Supabase.
Create a new file at app/auth/callback/route.js
and populate with the following:
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 supabase = createRouteHandlerClient({ cookies })
await supabase.auth.exchangeCodeForSession(code)
}
// URL to redirect to after sign in process completes
return NextResponse.redirect(requestUrl.origin)
}
Authentication#
Authentication can be initiated client or server-side. All of the supabase-js authentication strategies are supported with the Auth Helpers client.
Note: The authentication flow requires the Code Exchange Route to exchange a
code
for the user'ssession
.
Client-side#
Client Components can be used to trigger the authentication process from event handlers.
'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>
</>
)
}
Server-side#
The combination of Server Components and Server Actions can be used to trigger the authentication process from form submissions.
Note: Server Actions are currently in Alpha and likely to change. We recommend triggering the authentication flow client-side for production applications.
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { revalidatePath } from 'next/cache'
import { cookies } from 'next/headers'
export default async function Login() {
const handleSignUp = async (formData) => {
'use server'
const email = formData.get('email')
const password = formData.get('password')
const supabase = createServerActionClient({ cookies })
await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: 'http://localhost:3000/auth/callback',
},
})
revalidatePath('/')
}
const handleSignIn = async (formData) => {
'use server'
const email = formData.get('email')
const password = formData.get('password')
const supabase = createServerActionClient({ cookies })
await supabase.auth.signInWithPassword({
email,
password,
})
revalidatePath('/')
}
const handleSignOut = async () => {
'use server'
const supabase = createServerActionClient({ cookies })
await supabase.auth.signOut()
revalidatePath('/')
}
return (
<form action={handleSignUp}>
<input name="email" />
<input type="password" name="password" />
<button>Sign up</button>
<button formAction={handleSignIn}>Sign in</button>
<button formAction={handleSignOut}>Sign out</button>
</form>
)
}
Creating a Supabase Client#
Client Component#
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.
'use client'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
import { useEffect, useState } from 'react'
export default function Home() {
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>
}
check out this repo for more examples, including realtime subscriptions.
Singleton
The createClientComponentClient
function implements a Singleton pattern to simplify instantiating Supabase clients. If you need multiple Supabase instances across Client Components - for example, when using multiple schemas - you can pass an additional configuration option for { isSingleton: false }
to get a new client every time this function is called.
const supabase = createClientComponentClient({ isSingleton: false })
Server Component#
Server Components allow for asynchronous data to be fetched server-side.
Note: In order to use Supabase in Server Components, you need to have implemented the Middleware steps above.
import { cookies } from 'next/headers'
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
export default async function Home() {
const supabase = createServerComponentClient({ cookies })
const { data } = await supabase.from('todos').select()
return <pre>{JSON.stringify(data, null, 2)}</pre>
}
check out this repo for more examples, including redirecting unauthenticated users - protected pages.
Server Action#
Server Actions allow mutations to be performed server-side.
Note: Server Actions are currently in
alpha
so may change without notice.
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>
)
}
Route Handler#
Route 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.
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 supabase = createRouteHandlerClient({ cookies })
const { data } = await supabase.from('todos').insert({ title }).select()
return NextResponse.json(data)
}
Middleware#
See refreshing session example above.
More examples#
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 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:
supabase.auth.signUp({
email: 'jon@example.com',
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 withcreateMiddlewareClient
createBrowserSupabaseClient
has been replaced withcreateClientComponentClient
createServerComponentSupabaseClient
has been replaced withcreateServerComponentClient
createRouteHandlerSupabaseClient
has been replaced withcreateRouteHandlerClient
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!
"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 above.