How to Migrate from Supabase Auth Helpers to SSR package

Last edited: 1/14/2026

The auth-helpers packages are 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.

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

2. Create the library functions to create Supabase clients

1
// lib/supabase/client.ts
2
3
import { createBrowserClient } from '@supabase/ssr';
4
5
export function createClient() {
6
return createBrowserClient(
7
process.env.NEXT_PUBLIC_SUPABASE_URL!,
8
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!
9
);
10
}
11
12
// lib/supabase/server.ts
13
import { createServerClient } from '@supabase/ssr'
14
import { cookies } from 'next/headers'
15
16
export async function createClient() {
17
const cookieStore = await cookies()
18
19
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 }) => cookieStore.set(name, value, options))
30
} catch {
31
// The `setAll` method was called from a Server Component.
32
// This can be ignored if you have middleware refreshing
33
// user sessions.
34
}
35
},
36
},
37
}
38
)
39
}
40
41
// lib/supabase/proxy.ts
42
import { createServerClient } from '@supabase/ssr'
43
import { NextResponse, type NextRequest } from 'next/server'
44
45
export async function updateSession(request: NextRequest) {
46
let supabaseResponse = NextResponse.next({
47
request,
48
})
49
50
// With Fluid compute, don't put this client in a global environment
51
// variable. Always create a new one on each request.
52
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 }) => request.cookies.set(name, value))
62
supabaseResponse = NextResponse.next({
63
request,
64
})
65
cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options))
66
},
67
},
68
}
69
)
70
71
// Do not run code between createServerClient and
72
// supabase.auth.getClaims(). A simple mistake could make it very hard to debug
73
// issues with users being randomly logged out.
74
75
// IMPORTANT: If you remove getClaims() and you use server-side rendering
76
// with the Supabase client, your users may be randomly logged out.
77
const { data } = await supabase.auth.getClaims()
78
79
const user = data?.claims
80
81
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 page
87
const url = request.nextUrl.clone()
88
url.pathname = '/login'
89
return NextResponse.redirect(url)
90
}
91
92
// IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
93
// 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 changing
99
// the cookies!
100
// 4. Finally:
101
// return myNewResponse
102
// If this is not done, you may be causing the browser and server to go out
103
// of sync and terminate the user's session prematurely!
104
105
return supabaseResponse
106
}

3. Replace your proxy.ts file

1
// proxy.ts
2
3
import { type NextRequest } from "next/server"
4
import { updateSession } from "@/lib/supabase/proxy"
5
6
export async function proxy(request: NextRequest) {
7
return await updateSession(request)
8
}
9
10
export 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.ts
2
3
'use server';
4
5
import { revalidatePath } from 'next/cache';
6
import { redirect } from 'next/navigation';
7
8
import { createClient } from '@/utils/supabase/server';
9
10
export async function login(formData: FormData) {
11
const supabase = createClient();
12
13
// type-casting here for convenience
14
// in practice, you should validate your inputs
15
const data = {
16
email: formData.get('email') as string,
17
password: formData.get('password') as string,
18
};
19
20
const { error } = await supabase.auth.signInWithPassword(data)
21
22
if (error) {
23
redirect('/error');
24
}
25
26
revalidatePath('/', 'layout');
27
redirect('/');
28
}
29
30
export async function signup(formData: FormData) {
31
const supabase = createClient();
32
33
// type-casting here for convenience
34
// in practice, you should validate your inputs
35
const data = {
36
email: formData.get('email') as string,
37
password: formData.get('password') as string,
38
};
39
40
const { error } = await supabase.auth.signUp(data);
41
42
if (error) {
43
redirect('/error');
44
}
45
46
revalidatePath('/', 'layout');
47
redirect('/');
48
}

5. Utilize the server actions in your login page UI

1
// app/login/page.tsx
2
3
import { login, signup } from './actions';
4
5
export 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';
2
3
// replace this line
4
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
5
6
// with
7
import { createClient } from '@/utils/supabase/client';
8
9
export default async function Page() {
10
// replace this line
11
const supabase = createClientComponentClient<Database>();
12
13
// with
14
const supabase = createClient();
15
16
return...
17
}

7. Server components

1
// replace
2
import { cookies } from 'next/headers';
3
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
4
5
// with
6
import { createClient } from '@/utils/supabase/server';
7
8
export default async function Page() {
9
// replace
10
const cookieStore = cookies();
11
const supabase = createServerComponentClient<Database>({
12
cookies: () => cookieStore
13
});
14
15
// with
16
const supabase = createClient();
17
18
return...
19
}

8. Route handlers

1
// replace
2
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
3
import { cookies } from 'next/headers';
4
5
// with
6
import { createClient } from '@/utils/supabase/server';
7
8
export async function POST(request: Request) {
9
// replace
10
const supabase = createRouteHandlerClient<Database>({
11
cookies: () => cookieStore,
12
});
13
14
// with
15
const supabase = createClient();
16
17
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 SSR documentation.

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.