How to Migrate from Supabase Auth Helpers to SSR package
Last edited: 1/17/2025
The auth-helpers
packages are being deprecated and replaced with the @supabase/ssr
package. We recommend migrating to the @supabase/ssr
package as future bug fixes and feature releases are focused on the @supabase/ssr
package.
Here are the steps for you to migrate your application from the auth-helpers
package to @supabase/ssr
package.
Depending on your implementation, you may ignore some parts of this documentation and use your own implementation (i.e. using API routes vs. Server Actions). What’s important is you replace the clients provided by auth-helpers
with the utility functions created using clients provided by @supabase/ssr
.
1. Uninstall Supabase Auth helpers and install the Supabase SSR package
It’s important that you don’t use both auth-helpers-nextjs
and @supabase/ssr
packages in the same application to avoid running into authentication issues.
_10npm uninstall @supabase/auth-helpers-nextjs @supabase/supabase-js_10npm install @supabase/ssr @supabase/supabase-js
2. Create the utility functions to create Supabase clients.
_106// utils/supabase/client.ts_106_106import { createBrowserClient } from '@supabase/ssr';_106_106export function createClient() {_106 return createBrowserClient(_106 process.env.NEXT_PUBLIC_SUPABASE_URL!,_106 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!_106 );_106}_106_106// utils/supabase/server.ts_106import { createServerClient, type CookieOptions } from '@supabase/ssr';_106import { cookies } from 'next/headers';_106_106export function createClient() {_106 const cookieStore = cookies();_106_106 return createServerClient(_106 process.env.NEXT_PUBLIC_SUPABASE_URL!,_106 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,_106 {_106 cookies: {_106 getAll() {_106 return cookieStore.getAll()_106 },_106 setAll(cookiesToSet) {_106 try {_106 cookiesToSet.forEach(({ name, value, options }) =>_106 cookieStore.set(name, value, options)_106 )_106 } catch {_106 // The `setAll` method was called from a Server Component._106 // This can be ignored if you have middleware refreshing_106 // user sessions._106 }_106 },_106 },_106 }_106 );_106}_106_106// utils/supabase/middleware.ts_106import { createServerClient } from '@supabase/ssr';_106import { NextResponse, type NextRequest } from 'next/server';_106_106export async function updateSession(request: NextRequest) {_106 let supabaseResponse = NextResponse.next({_106 request,_106 });_106_106 const supabase = createServerClient(_106 process.env.NEXT_PUBLIC_SUPABASE_URL!,_106 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,_106 {_106 cookies: {_106 getAll() {_106 return request.cookies.getAll()_106 },_106 setAll(cookiesToSet) {_106 cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))_106 supabaseResponse = NextResponse.next({_106 request,_106 })_106 cookiesToSet.forEach(({ name, value, options }) =>_106 supabaseResponse.cookies.set(name, value, options)_106 )_106 },_106 },_106 }_106 );_106_106 // IMPORTANT: Avoid writing any logic between createServerClient and_106 // supabase.auth.getUser(). A simple mistake could make it very hard to debug_106 // issues with users being randomly logged out._106_106 const {_106 data: { user },_106 } = await supabase.auth.getUser();_106_106 if (_106 !user &&_106 !request.nextUrl.pathname.startsWith('/login') &&_106 !request.nextUrl.pathname.startsWith('/auth')_106 ) {_106 // no user, potentially respond by redirecting the user to the login page_106 const url = request.nextUrl.clone();_106 url.pathname = '/login';_106 return NextResponse.redirect(url);_106 }_106_106 // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're_106 // creating a new response object with NextResponse.next() make sure to:_106 // 1. Pass the request in it, like so:_106 // const myNewResponse = NextResponse.next({ request })_106 // 2. Copy over the cookies, like so:_106 // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())_106 // 3. Change the myNewResponse object to fit your needs, but avoid changing_106 // the cookies!_106 // 4. Finally:_106 // return myNewResponse_106 // If this is not done, you may be causing the browser and server to go out_106 // of sync and terminate the user's session prematurely!_106_106 return supabaseResponse;_106}
3. Replace your middleware.ts file
_21// middleware.ts_21_21import { type NextRequest } from 'next/server';_21import { updateSession } from '@/utils/supabase/middleware';_21_21export async function middleware(request: NextRequest) {_21 return await updateSession(request);_21}_21_21export const config = {_21 matcher: [_21 /*_21 * Match all request paths except for the ones starting with:_21 * - _next/static (static files)_21 * - _next/image (image optimization files)_21 * - favicon.ico (favicon file)_21 * Feel free to modify this pattern to include more paths._21 */_21 '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',_21 ],_21};
4. Create your server actions to handle login and sign up.
_48// app/login/actions.ts_48_48'use server';_48_48import { revalidatePath } from 'next/cache';_48import { redirect } from 'next/navigation';_48_48import { createClient } from '@/utils/supabase/server';_48_48export async function login(formData: FormData) {_48 const supabase = createClient();_48_48 // type-casting here for convenience_48 // in practice, you should validate your inputs_48 const data = {_48 email: formData.get('email') as string,_48 password: formData.get('password') as string,_48 };_48_48 const { error } = await supabase.auth.signInWithPassword(data)_48_48 if (error) {_48 redirect('/error');_48 }_48_48 revalidatePath('/', 'layout');_48 redirect('/');_48}_48_48export async function signup(formData: FormData) {_48 const supabase = createClient();_48_48 // type-casting here for convenience_48 // in practice, you should validate your inputs_48 const data = {_48 email: formData.get('email') as string,_48 password: formData.get('password') as string,_48 };_48_48 const { error } = await supabase.auth.signUp(data);_48_48 if (error) {_48 redirect('/error');_48 }_48_48 revalidatePath('/', 'layout');_48 redirect('/');_48}
5. Utilize the server actions in your login page UI.
_16// app/login/page.tsx_16_16import { login, signup } from './actions';_16_16export default function LoginPage() {_16 return (_16 <form>_16 <label htmlFor="email">Email:</label>_16 <input id="email" name="email" type="email" required />_16 <label htmlFor="password">Password:</label>_16 <input id="password" name="password" type="password" required />_16 <button formAction={login}>Log in</button>_16 <button formAction={signup}>Sign up</button>_16 </form>_16 );_16}
6. Client components
_17'use client';_17_17// replace this line_17import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';_17_17// with_17import { createClient } from '@/utils/supabase/client';_17_17export default async function Page() {_17 // replace this line_17 const supabase = createClientComponentClient<Database>();_17_17 // with_17 const supabase = createClient();_17_17 return..._17}
7. Server components
_19// replace_19import { cookies } from 'next/headers';_19import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';_19_19// with_19import { createClient } from '@/utils/supabase/server';_19_19export default async function Page() {_19 // replace_19 const cookieStore = cookies();_19 const supabase = createServerComponentClient<Database>({_19 cookies: () => cookieStore_19 });_19_19 // with_19 const supabase = createClient();_19_19 return..._19}
8. Route handlers
_18// replace_18import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';_18import { cookies } from 'next/headers';_18_18// with_18import { createClient } from '@/utils/supabase/server';_18_18export async function POST(request: Request) {_18 // replace_18 const supabase = createRouteHandlerClient<Database>({_18 cookies: () => cookieStore,_18 });_18_18 // with_18 const supabase = createClient();_18_18 return..._18}
Likewise, you can replace the clients created with @supabase/auth-helpers-nextjs
with utility functions you created with @supabase/ssr
.
createMiddlewareClient
→ createServerClient
createClientComponentClient
→ createBrowserClient
createServerComponentClient
→ createServerClient
createRouteHandlerClient
→ createServerClient
You can find more clear and concise examples of creating clients in our documentation here.
If you have any feedback about this guide, please provide them as a comment below. If you find any issues or have feedback for the @supabase/ssr
client, please post them as an issue in @supabase/ssr
repo.
As always, our GitHub community and Discord channel are open for technical discussions and resolving your issues.