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#

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

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 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!

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