How to Migrate from Supabase Auth Helpers to SSR package
Last edited: 12/1/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.
1npm uninstall @supabase/auth-helpers-nextjs @supabase/supabase-js2npm install @supabase/ssr @supabase/supabase-js2. Create the utility functions to create Supabase clients
1// utils/supabase/client.ts23import { createBrowserClient } from '@supabase/ssr';45export function createClient() {6 return createBrowserClient(7 process.env.NEXT_PUBLIC_SUPABASE_URL!,8 process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!9 );10}1112// utils/supabase/server.ts13import { createServerClient, type CookieOptions } from '@supabase/ssr';14import { cookies } from 'next/headers';1516export function createClient() {17 const cookieStore = cookies();1819 return createServerClient(20 process.env.NEXT_PUBLIC_SUPABASE_URL!,21 process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,22 {23 cookies: {24 getAll() {25 return cookieStore.getAll()26 },27 setAll(cookiesToSet) {28 try {29 cookiesToSet.forEach(({ name, value, options }) =>30 cookieStore.set(name, value, options)31 )32 } catch {33 // The `setAll` method was called from a Server Component.34 // This can be ignored if you have proxy refreshing35 // user sessions.36 }37 },38 },39 }40 );41}4243// utils/supabase/proxy.ts44import { createServerClient } from '@supabase/ssr';45import { NextResponse, type NextRequest } from 'next/server';4647export async function updateSession(request: NextRequest) {48 let supabaseResponse = NextResponse.next({49 request,50 });5152 const supabase = createServerClient(53 process.env.NEXT_PUBLIC_SUPABASE_URL!,54 process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,55 {56 cookies: {57 getAll() {58 return request.cookies.getAll()59 },60 setAll(cookiesToSet) {61 cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))62 supabaseResponse = NextResponse.next({63 request,64 })65 cookiesToSet.forEach(({ name, value, options }) =>66 supabaseResponse.cookies.set(name, value, options)67 )68 },69 },70 }71 );7273 // IMPORTANT: Avoid writing any logic between createServerClient and74 // supabase.auth.getUser(). A simple mistake could make it very hard to debug75 // issues with users being randomly logged out.7677 const {78 data: { user },79 } = await supabase.auth.getUser();8081 if (82 !user &&83 !request.nextUrl.pathname.startsWith('/login') &&84 !request.nextUrl.pathname.startsWith('/auth')85 ) {86 // no user, potentially respond by redirecting the user to the login page87 const url = request.nextUrl.clone();88 url.pathname = '/login';89 return NextResponse.redirect(url);90 }9192 // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're93 // creating a new response object with NextResponse.next() make sure to:94 // 1. Pass the request in it, like so:95 // const myNewResponse = NextResponse.next({ request })96 // 2. Copy over the cookies, like so:97 // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())98 // 3. Change the myNewResponse object to fit your needs, but avoid changing99 // the cookies!100 // 4. Finally:101 // return myNewResponse102 // If this is not done, you may be causing the browser and server to go out103 // of sync and terminate the user's session prematurely!104105 return supabaseResponse;106}3. Replace your proxy.ts file
1// proxy.ts23import { type NextRequest } from 'next/server';4import { updateSession } from '@/utils/supabase/proxy';56export async function proxy(request: NextRequest) {7 return await updateSession(request);8}910export const config = {11 matcher: [12 /*13 * Match all request paths except for the ones starting with:14 * - _next/static (static files)15 * - _next/image (image optimization files)16 * - favicon.ico (favicon file)17 * Feel free to modify this pattern to include more paths.18 */19 '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',20 ],21};4. Create your server actions to handle login and sign up
1// app/login/actions.ts23'use server';45import { revalidatePath } from 'next/cache';6import { redirect } from 'next/navigation';78import { createClient } from '@/utils/supabase/server';910export async function login(formData: FormData) {11 const supabase = createClient();1213 // type-casting here for convenience14 // in practice, you should validate your inputs15 const data = {16 email: formData.get('email') as string,17 password: formData.get('password') as string,18 };1920 const { error } = await supabase.auth.signInWithPassword(data)2122 if (error) {23 redirect('/error');24 }2526 revalidatePath('/', 'layout');27 redirect('/');28}2930export async function signup(formData: FormData) {31 const supabase = createClient();3233 // type-casting here for convenience34 // in practice, you should validate your inputs35 const data = {36 email: formData.get('email') as string,37 password: formData.get('password') as string,38 };3940 const { error } = await supabase.auth.signUp(data);4142 if (error) {43 redirect('/error');44 }4546 revalidatePath('/', 'layout');47 redirect('/');48}5. Utilize the server actions in your login page UI
1// app/login/page.tsx23import { login, signup } from './actions';45export default function LoginPage() {6 return (7 <form>8 <label htmlFor="email">Email:</label>9 <input id="email" name="email" type="email" required />10 <label htmlFor="password">Password:</label>11 <input id="password" name="password" type="password" required />12 <button formAction={login}>Log in</button>13 <button formAction={signup}>Sign up</button>14 </form>15 );16}6. Client components
1'use client';23// replace this line4import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';56// with7import { createClient } from '@/utils/supabase/client';89export default async function Page() {10 // replace this line11 const supabase = createClientComponentClient<Database>();1213 // with14 const supabase = createClient();1516 return...17}7. Server components
1// replace2import { cookies } from 'next/headers';3import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';45// with6import { createClient } from '@/utils/supabase/server';78export default async function Page() {9 // replace10 const cookieStore = cookies();11 const supabase = createServerComponentClient<Database>({12 cookies: () => cookieStore13 });1415 // with16 const supabase = createClient();1718 return...19}8. Route handlers
1// replace2import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';3import { cookies } from 'next/headers';45// with6import { createClient } from '@/utils/supabase/server';78export async function POST(request: Request) {9 // replace10 const supabase = createRouteHandlerClient<Database>({11 cookies: () => cookieStore,12 });1314 // with15 const supabase = createClient();1617 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, provide them as a comment below. If you find any issues or have feedback for the @supabase/ssr client, 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.