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.


_10
npm uninstall @supabase/auth-helpers-nextjs @supabase/supabase-js
_10
npm install @supabase/ssr @supabase/supabase-js

2. Create the utility functions to create Supabase clients.


_106
// utils/supabase/client.ts
_106
_106
import { createBrowserClient } from '@supabase/ssr';
_106
_106
export 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
_106
import { createServerClient, type CookieOptions } from '@supabase/ssr';
_106
import { cookies } from 'next/headers';
_106
_106
export 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
_106
import { createServerClient } from '@supabase/ssr';
_106
import { NextResponse, type NextRequest } from 'next/server';
_106
_106
export 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
_21
import { type NextRequest } from 'next/server';
_21
import { updateSession } from '@/utils/supabase/middleware';
_21
_21
export async function middleware(request: NextRequest) {
_21
return await updateSession(request);
_21
}
_21
_21
export 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
_48
import { revalidatePath } from 'next/cache';
_48
import { redirect } from 'next/navigation';
_48
_48
import { createClient } from '@/utils/supabase/server';
_48
_48
export 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
_48
export 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
_16
import { login, signup } from './actions';
_16
_16
export 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
_17
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
_17
_17
// with
_17
import { createClient } from '@/utils/supabase/client';
_17
_17
export 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
_19
import { cookies } from 'next/headers';
_19
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
_19
_19
// with
_19
import { createClient } from '@/utils/supabase/server';
_19
_19
export 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
_18
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
_18
import { cookies } from 'next/headers';
_18
_18
// with
_18
import { createClient } from '@/utils/supabase/server';
_18
_18
export 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.

createMiddlewareClientcreateServerClient createClientComponentClientcreateBrowserClient createServerComponentClientcreateServerClient createRouteHandlerClientcreateServerClient

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.