Home

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#


_10
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:

.env.local

_10
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
_10
NEXT_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:

middleware.js

_10
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
_10
import { NextResponse } from 'next/server'
_10
_10
export async function middleware(req) {
_10
const res = NextResponse.next()
_10
const supabase = createMiddlewareClient({ req, res })
_10
await supabase.auth.getSession()
_10
return res
_10
}

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:

app/auth/callback/route.js

_16
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
_16
import { cookies } from 'next/headers'
_16
import { NextResponse } from 'next/server'
_16
_16
export async function GET(request) {
_16
const requestUrl = new URL(request.url)
_16
const code = requestUrl.searchParams.get('code')
_16
_16
if (code) {
_16
const supabase = createRouteHandlerClient({ cookies })
_16
await supabase.auth.exchangeCodeForSession(code)
_16
}
_16
_16
// URL to redirect to after sign in process completes
_16
return NextResponse.redirect(requestUrl.origin)
_16
}

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's session.

Client-side#

Client Components can be used to trigger the authentication process from event handlers.

app/login.js

_51
'use client'
_51
_51
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
_51
import { useRouter } from 'next/navigation'
_51
import { useState } from 'react'
_51
_51
export default function Login() {
_51
const [email, setEmail] = useState('')
_51
const [password, setPassword] = useState('')
_51
const router = useRouter()
_51
const supabase = createClientComponentClient()
_51
_51
const handleSignUp = async () => {
_51
await supabase.auth.signUp({
_51
email,
_51
password,
_51
options: {
_51
emailRedirectTo: `${location.origin}/auth/callback`,
_51
},
_51
})
_51
router.refresh()
_51
}
_51
_51
const handleSignIn = async () => {
_51
await supabase.auth.signInWithPassword({
_51
email,
_51
password,
_51
})
_51
router.refresh()
_51
}
_51
_51
const handleSignOut = async () => {
_51
await supabase.auth.signOut()
_51
router.refresh()
_51
}
_51
_51
return (
_51
<>
_51
<input name="email" onChange={(e) => setEmail(e.target.value)} value={email} />
_51
<input
_51
type="password"
_51
name="password"
_51
onChange={(e) => setPassword(e.target.value)}
_51
value={password}
_51
/>
_51
<button onClick={handleSignUp}>Sign up</button>
_51
<button onClick={handleSignIn}>Sign in</button>
_51
<button onClick={handleSignOut}>Sign out</button>
_51
</>
_51
)
_51
}

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.

app/login.js

_53
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
_53
import { revalidatePath } from 'next/cache'
_53
import { cookies } from 'next/headers'
_53
_53
export default async function Login() {
_53
const handleSignUp = async (formData) => {
_53
'use server'
_53
const email = formData.get('email')
_53
const password = formData.get('password')
_53
_53
const supabase = createServerActionClient({ cookies })
_53
await supabase.auth.signUp({
_53
email,
_53
password,
_53
options: {
_53
emailRedirectTo: 'http://localhost:3000/auth/callback',
_53
},
_53
})
_53
_53
revalidatePath('/')
_53
}
_53
_53
const handleSignIn = async (formData) => {
_53
'use server'
_53
const email = formData.get('email')
_53
const password = formData.get('password')
_53
_53
const supabase = createServerActionClient({ cookies })
_53
await supabase.auth.signInWithPassword({
_53
email,
_53
password,
_53
})
_53
_53
revalidatePath('/')
_53
}
_53
_53
const handleSignOut = async () => {
_53
'use server'
_53
const supabase = createServerActionClient({ cookies })
_53
await supabase.auth.signOut()
_53
revalidatePath('/')
_53
}
_53
_53
return (
_53
<form action={handleSignUp}>
_53
<input name="email" />
_53
<input type="password" name="password" />
_53
<button>Sign up</button>
_53
<button formAction={handleSignIn}>Sign in</button>
_53
<button formAction={handleSignOut}>Sign out</button>
_53
</form>
_53
)
_53
}

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.

app/client/page.jsx

_20
'use client'
_20
_20
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
_20
import { useEffect, useState } from 'react'
_20
_20
export default function Home() {
_20
const [todos, setTodos] = useState()
_20
const supabase = createClientComponentClient()
_20
_20
useEffect(() => {
_20
const getData = async () => {
_20
const { data } = await supabase.from('todos').select()
_20
setTodos(data)
_20
}
_20
_20
getData()
_20
}, [])
_20
_20
return todos ? <pre>{JSON.stringify(todos, null, 2)}</pre> : <p>Loading todos...</p>
_20
}

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.


_10
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.

app/page.jsx

_10
import { cookies } from 'next/headers'
_10
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
_10
_10
export default async function Home() {
_10
const supabase = createServerComponentClient({ cookies })
_10
const { data } = await supabase.from('todos').select()
_10
return <pre>{JSON.stringify(data, null, 2)}</pre>
_10
}

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.

app/new-post.jsx

_20
import { cookies } from 'next/headers'
_20
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
_20
import { revalidatePath } from 'next/cache'
_20
_20
export default async function NewTodo() {
_20
const addTodo = async (formData) => {
_20
'use server'
_20
_20
const title = formData.get('title')
_20
const supabase = createServerActionClient({ cookies })
_20
await supabase.from('todos').insert({ title })
_20
revalidatePath('/')
_20
}
_20
_20
return (
_20
<form action={addTodo}>
_20
<input name="title" />
_20
</form>
_20
)
_20
}

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.

app/api/todos/route.jsx

_10
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
_10
import { NextResponse } from 'next/server'
_10
import { cookies } from 'next/headers'
_10
_10
export async function POST(request) {
_10
const { title } = await request.json()
_10
const supabase = createRouteHandlerClient({ cookies })
_10
const { data } = await supabase.from('todos').insert({ title }).select()
_10
return NextResponse.json(data)
_10
}

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:


_10
supabase.auth.signUp({
_10
email: 'jon@example.com',
_10
password: 'sup3rs3cur3',
_10
options: {
_10
emailRedirectTo: 'http://localhost:3000/auth/callback',
_10
},
_10
})

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!


_10
"use client";
_10
_10
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
_10
_10
export default function() {
_10
const supabase = createClientComponentClient();
_10
return ...
_10
}

For an example of creating multiple Supabase clients, check Singleton section above.