Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Supabase SSR
https://github.com/supabase/ssr
Admin
Supabase clients for use in SSR frameworks, providing a framework-agnostic way to create a Supabase
...
Tokens:
22,320
Snippets:
152
Trust Score:
9.5
Update:
5 days ago
Context
Skills
Chat
Benchmark
91.8
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# @supabase/ssr `@supabase/ssr` is a framework-agnostic library for using the Supabase JavaScript client in server-side rendering (SSR) frameworks. It provides unified cookie-based session management that works consistently across Next.js, Remix, SvelteKit, and other SSR environments. This package consolidates and replaces the deprecated `@supabase/auth-helpers-*` packages. The library handles the complexity of persisting authentication sessions via cookies, including automatic cookie chunking for large session payloads, Base64-URL encoding for cross-browser compatibility, and proper session refresh handling. It provides two main client factories: `createBrowserClient` for client-side usage with automatic `document.cookie` integration, and `createServerClient` for server-side middleware, routes, and components with configurable cookie handlers. ## createBrowserClient Creates a Supabase client for browser environments with automatic cookie management using `document.cookie`. Implements a singleton pattern by default to prevent multiple client instances. ```typescript import { createBrowserClient } from '@supabase/ssr' // Basic usage - uses document.cookie automatically const supabase = createBrowserClient( 'https://your-project.supabase.co', 'your-anon-key' ) // Get user session const { data: { session }, error } = await supabase.auth.getSession() if (session) { console.log('User:', session.user.email) } // Sign in with email/password const { data, error: signInError } = await supabase.auth.signInWithPassword({ email: 'user@example.com', password: 'password123' }) // Custom cookie handling (optional) const supabaseCustom = createBrowserClient( 'https://your-project.supabase.co', 'your-anon-key', { cookies: { getAll: () => { // Return all cookies as { name, value }[] return document.cookie.split(';').map(c => { const [name, ...rest] = c.trim().split('=') return { name, value: rest.join('=') } }) }, setAll: (cookies) => { // Set multiple cookies at once cookies.forEach(({ name, value, options }) => { document.cookie = `${name}=${value}; path=${options.path}; max-age=${options.maxAge}` }) } }, isSingleton: true // Default: true in browser } ) ``` ## createServerClient Creates a Supabase client for server-side environments (middleware, routes, server components). Requires explicit cookie handlers and supports lazy session initialization. ```typescript import { createServerClient } from '@supabase/ssr' // Next.js Middleware example // middleware.ts import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(request: NextRequest) { const response = NextResponse.next() const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll: () => { return request.cookies.getAll().map(cookie => ({ name: cookie.name, value: cookie.value })) }, setAll: (cookies) => { cookies.forEach(({ name, value, options }) => { response.cookies.set(name, value, options) }) } } } ) // Refresh session if expired - required for Server Components const { data: { user } } = await supabase.auth.getUser() // Protect routes if (!user && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)) } return response } // Next.js Server Component example // app/page.tsx import { cookies } from 'next/headers' import { createServerClient } from '@supabase/ssr' export default async function Page() { const cookieStore = await cookies() const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll: () => cookieStore.getAll(), // setAll omitted - cookies cannot be set from Server Components } } ) const { data: { user } } = await supabase.auth.getUser() return <div>Hello {user?.email}</div> } // Next.js Route Handler example // app/api/user/route.ts import { cookies } from 'next/headers' import { createServerClient } from '@supabase/ssr' import { NextResponse } from 'next/server' export async function GET() { const cookieStore = await cookies() const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll: () => cookieStore.getAll(), setAll: (cookiesToSet) => { cookiesToSet.forEach(({ name, value, options }) => { cookieStore.set(name, value, options) }) } } } ) const { data: { user }, error } = await supabase.auth.getUser() if (error || !user) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } return NextResponse.json({ user }) } ``` ## Cookie Configuration Options Configure cookie behavior including custom names, encoding strategies, and serialization options. ```typescript import { createServerClient, createBrowserClient } from '@supabase/ssr' // Custom cookie options const supabase = createServerClient( 'https://your-project.supabase.co', 'your-anon-key', { cookies: { getAll: () => cookieStore.getAll(), setAll: (cookies) => { cookies.forEach(({ name, value, options }) => { cookieStore.set(name, value, options) }) } }, // Custom cookie name (default: 'sb-<project-ref>-auth-token') cookieOptions: { name: 'my-app-auth', path: '/', sameSite: 'lax', httpOnly: false, maxAge: 60 * 60 * 24 * 400 // 400 days }, // Encoding: 'base64url' (default) or 'raw' cookieEncoding: 'base64url' } ) // Tokens-only encoding (experimental) - stores only access/refresh tokens in cookies // Reduces cookie size by storing user object in localStorage/memory const supabaseBrowser = createBrowserClient( 'https://your-project.supabase.co', 'your-anon-key', { cookies: { encode: 'tokens-only', // Experimental feature getAll: () => document.cookie.split(';').map(c => { const [name, ...v] = c.trim().split('=') return { name, value: v.join('=') } }), setAll: (cookies) => { cookies.forEach(({ name, value, options }) => { document.cookie = `${name}=${value}; path=${options.path}; max-age=${options.maxAge}` }) } } } ) ``` ## parseCookieHeader Utility function to parse the `Cookie` HTTP header into an array of name-value objects. ```typescript import { parseCookieHeader } from '@supabase/ssr' // Parse Cookie header from HTTP request const cookieHeader = 'sb-auth-token=abc123; theme=dark; session_id=xyz789' const cookies = parseCookieHeader(cookieHeader) console.log(cookies) // Output: // [ // { name: 'sb-auth-token', value: 'abc123' }, // { name: 'theme', value: 'dark' }, // { name: 'session_id', value: 'xyz789' } // ] // Use with createServerClient in generic HTTP handlers import { createServerClient } from '@supabase/ssr' function handleRequest(req, res) { const cookies = parseCookieHeader(req.headers.cookie || '') const supabase = createServerClient( process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY, { cookies: { getAll: () => cookies, setAll: (cookiesToSet) => { cookiesToSet.forEach(({ name, value, options }) => { res.setHeader('Set-Cookie', `${name}=${value}; Path=${options.path}; Max-Age=${options.maxAge}; SameSite=${options.sameSite}` ) }) } } } ) return supabase } ``` ## serializeCookieHeader Utility function to serialize a cookie into a valid `Set-Cookie` header string. ```typescript import { serializeCookieHeader } from '@supabase/ssr' // Create Set-Cookie header const setCookieHeader = serializeCookieHeader( 'session', 'abc123', { path: '/', maxAge: 60 * 60 * 24 * 7, // 7 days sameSite: 'lax', httpOnly: true, secure: true } ) console.log(setCookieHeader) // Output: 'session=abc123; Max-Age=604800; Path=/; HttpOnly; Secure; SameSite=Lax' // Use in custom HTTP response function setAuthCookie(res, sessionToken) { const header = serializeCookieHeader('auth-token', sessionToken, { path: '/', maxAge: 60 * 60 * 24 * 400, sameSite: 'lax', httpOnly: false }) res.setHeader('Set-Cookie', header) } ``` ## SvelteKit Integration Example integration with SvelteKit using hooks and load functions. ```typescript // src/hooks.server.ts import { createServerClient } from '@supabase/ssr' import type { Handle } from '@sveltejs/kit' export const handle: Handle = async ({ event, resolve }) => { const supabase = createServerClient( import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_ANON_KEY, { cookies: { getAll: () => event.cookies.getAll(), setAll: (cookies) => { cookies.forEach(({ name, value, options }) => { event.cookies.set(name, value, { ...options, path: options.path ?? '/' }) }) } } } ) // Refresh session const { data: { session } } = await supabase.auth.getSession() event.locals.supabase = supabase event.locals.session = session return resolve(event) } // src/routes/+page.server.ts import type { PageServerLoad } from './$types' export const load: PageServerLoad = async ({ locals }) => { const { data: { user } } = await locals.supabase.auth.getUser() if (!user) { return { user: null } } const { data: profile } = await locals.supabase .from('profiles') .select('*') .eq('id', user.id) .single() return { user, profile } } ``` ## Remix Integration Example integration with Remix using loaders and actions. ```typescript // app/utils/supabase.server.ts import { createServerClient } from '@supabase/ssr' import { parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' export function createSupabaseClient(request: Request) { const headers = new Headers() const cookies = parseCookieHeader(request.headers.get('Cookie') || '') const supabase = createServerClient( process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll: () => cookies, setAll: (cookiesToSet) => { cookiesToSet.forEach(({ name, value, options }) => { headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) }) } } } ) return { supabase, headers } } // app/routes/_index.tsx import { json, type LoaderFunctionArgs } from '@remix-run/node' import { useLoaderData } from '@remix-run/react' import { createSupabaseClient } from '~/utils/supabase.server' export async function loader({ request }: LoaderFunctionArgs) { const { supabase, headers } = createSupabaseClient(request) const { data: { user } } = await supabase.auth.getUser() if (!user) { return json({ user: null, posts: [] }, { headers }) } const { data: posts } = await supabase .from('posts') .select('*') .eq('user_id', user.id) return json({ user, posts }, { headers }) } export default function Index() { const { user, posts } = useLoaderData<typeof loader>() return ( <div> <h1>Welcome {user?.email}</h1> <ul> {posts.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> </div> ) } ``` ## Authentication Flow Examples Common authentication patterns including sign-in, sign-up, sign-out, and OAuth. ```typescript import { createBrowserClient } from '@supabase/ssr' const supabase = createBrowserClient( 'https://your-project.supabase.co', 'your-anon-key' ) // Email/Password Sign Up async function signUp(email: string, password: string) { const { data, error } = await supabase.auth.signUp({ email, password, options: { emailRedirectTo: `${window.location.origin}/auth/callback` } }) if (error) { console.error('Sign up error:', error.message) return null } return data.user } // Email/Password Sign In async function signIn(email: string, password: string) { const { data, error } = await supabase.auth.signInWithPassword({ email, password }) if (error) { console.error('Sign in error:', error.message) return null } return data.session } // OAuth Sign In (Google, GitHub, etc.) async function signInWithOAuth(provider: 'google' | 'github' | 'discord') { const { data, error } = await supabase.auth.signInWithOAuth({ provider, options: { redirectTo: `${window.location.origin}/auth/callback` } }) if (error) { console.error('OAuth error:', error.message) } // User will be redirected to OAuth provider } // Sign Out async function signOut() { const { error } = await supabase.auth.signOut() if (error) { console.error('Sign out error:', error.message) } // Session cookies will be cleared automatically window.location.href = '/login' } // Get verified user (recommended for authorization) async function getVerifiedUser() { // getUser() contacts Auth server - use for authorization decisions const { data: { user }, error } = await supabase.auth.getUser() if (error || !user) { return null } return user } // Get session from cookies (NOT verified - don't use for authorization) async function getSessionFromCookies() { // getSession() reads directly from cookies without verification // The user object is NOT verified - could be spoofed const { data: { session } } = await supabase.auth.getSession() return session } // Listen to auth state changes supabase.auth.onAuthStateChange((event, session) => { console.log('Auth event:', event) switch (event) { case 'SIGNED_IN': console.log('User signed in:', session?.user.email) break case 'SIGNED_OUT': console.log('User signed out') window.location.href = '/login' break case 'TOKEN_REFRESHED': console.log('Token refreshed') break case 'USER_UPDATED': console.log('User updated:', session?.user) break } }) ``` ## Summary `@supabase/ssr` provides the essential building blocks for implementing authentication in server-side rendered applications. The library's main use cases include: protecting server-rendered routes via middleware patterns, maintaining consistent session state between browser and server, and implementing OAuth flows with proper cookie handling. The middleware pattern is critical - it ensures sessions are refreshed before pages render and that updated tokens are properly sent back to the browser via `Set-Cookie` headers. When integrating with any SSR framework, the key pattern is to create a server client in middleware with both `getAll` and `setAll` cookie methods, call `getUser()` or `getSession()` early in the request lifecycle to trigger any necessary token refresh, and ensure the response includes the updated cookies. For server components where cookies cannot be set, always rely on middleware to handle session updates. Use `getUser()` for authorization decisions as it contacts the Auth server for verification, while `getSession()` reads unverified data directly from cookies and should only be used for non-sensitive display purposes.