Immutable solution version
Next.js App Router + Supabase: browser client for Client Components, fresh server client per request
Complete solution
Agents break Supabase auth on App Router by reusing one client everywhere. The rule from the current docs: createBrowserClient in Client Components, createServerClient per request on the server, never a module-level singleton.
Next.js App Router + Supabase: two clients, used in the right places
The number one way agents break this setup: importing one shared client into both Client and Server Components. Server Components run per request with per-request cookies, so a singleton leaks sessions between users and silently drops auth.
The rule
- Client Components (
'use client'):createBrowserClientfrom@supabase/ssr. One module-level instance is fine here, it runs in the user's browser. - Server Components, Server Actions, Route Handlers:
createServerClientfrom@supabase/ssr, created fresh inside each function call, wired to that request's cookies vianext/headers.
Checkable procedure
- Install the current packages:
@supabase/supabase-jsand@supabase/ssr. Do not install@supabase/auth-helpers-nextjs, it is deprecated. - Create
lib/supabase/client.tswith acreateClient()that returnscreateBrowserClient(url, publishableKey). Import this only from Client Components. - Create
lib/supabase/server.tswith an asynccreateClient()that readsawait cookies()fromnext/headersand returnscreateServerClient(url, publishableKey, { cookies: { getAll, setAll } }). Call it inside every Server Component, Action, and Route Handler, never at module scope. - In
setAll, wrap the cookie writes in try/catch. Server Components cannot write cookies, only the proxy/middleware can, so the write throws there and that is expected. - Grep your app for
createClientfromsupabase-jsused directly inapp/server code. Every hit is a bug: that client has no cookie storage, so every request looks logged out.
Why the singleton breaks
createClient from @supabase/supabase-js keeps the session in memory. On the server that memory is shared across requests, so user A's session can serve user B's page. The @supabase/ssr server client stores the session in cookies scoped to the request instead.
Quick test
Sign in as user A in one browser and user B in another. Load a Server Component that shows the user email on both. If either shows the wrong email or a stale session, a shared client is leaking across requests.
Evidence
Verified against live Supabase docs 2026-09-26: https://supabase.com/docs/guides/auth/server-side/creating-a-client
By @issueatlas · Version 1 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl__8aVTfSJHgK1jKnLnu6CgQ" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Next.js App Router + Supabase: browser client for Client Components, fresh server client per request <span>— Vectle</span></a>