---
title: "Supabase Client Utilities"
description: "Create browser and server Supabase clients using @supabase/ssr for SSR-compatible authentication in Next.js 16."
canonical_url: "https://vercel.com/academy/subscription-store/supabase-client-utilities"
md_url: "https://vercel.com/academy/subscription-store/supabase-client-utilities.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-11T07:44:06.269Z"
content_type: "lesson"
course: "subscription-store"
course_title: "Launch a Subscription Store with Vercel and Stripe"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Supabase Client Utilities

# Supabase Client Utilities

Next.js apps run in two environments: the browser and the server. Each needs its own Supabase client with different cookie handling. The `@supabase/ssr` package provides SSR-compatible clients that keep auth state synchronized.

## Outcome

Create browser and server Supabase clients that handle authentication cookies correctly in both environments.

## Fast Track

1. Implement `utils/supabase/client.ts` with `createBrowserClient`
2. Implement `utils/supabase/server.ts` with `createServerClient`
3. Verify no TypeScript errors in both files

## Hands-on Exercise 1.3

Implement the Supabase client utilities (TODO stubs are provided):

**Requirements:**

1. Implement the browser client using `createBrowserClient`
2. Implement the server client using `createServerClient` with cookie handling
3. Both files export a `createSupabaseClient` function
4. Handle the async cookies API in the server client

**Implementation hints:**

- The browser client is simple - just pass the URL and anon key
- The server client needs cookie `getAll` and `setAll` callbacks
- Use `await cookies()` in Next.js 16 (it's now async)
- Wrap `setAll` in try/catch for Server Components

## Try It

1. **Check the browser client:**
   ```bash
   # Open the file and verify it exports createSupabaseClient
   cat utils/supabase/client.ts
   ```

2. **Check the server client:**
   ```bash
   cat utils/supabase/server.ts
   ```

3. **Verify no TypeScript errors:**

   ```bash
   pnpm build
   ```

   If the build succeeds (or only fails on missing Stripe config), your clients are correct.

4. **Test in dev:**

   ```bash
   pnpm dev
   ```

   Visit <http://localhost:3000> - the page should load without console errors about Supabase.

## Commit

```bash
git add -A
git commit -m "feat(auth): add Supabase client utilities"
```

## Done-When

- [ ] `utils/supabase/client.ts` exports `createSupabaseClient`
- [ ] `utils/supabase/server.ts` exports async `createSupabaseClient`
- [ ] Server client handles cookie operations with `getAll` and `setAll`
- [ ] No TypeScript errors in either file
- [ ] Dev server runs without Supabase-related errors

## Solution

**Browser Client: `utils/supabase/client.ts`**

Replace the TODO stub with the implementation:

```typescript title="utils/supabase/client.ts"
import { createBrowserClient } from "@supabase/ssr";

export function createSupabaseClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
}
```

The browser client is straightforward. `createBrowserClient` handles cookies automatically using `document.cookie`.

**Server Client: `utils/supabase/server.ts`**

Replace the TODO stub with the implementation:

```typescript title="utils/supabase/server.ts"
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

export async function createSupabaseClient() {
  const cookieStore = await cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            );
          } catch {
            // The `setAll` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  );
}
```

The server client is more involved:

1. **`await cookies()`** - In Next.js 16, the cookies API is async
2. **`getAll()`** - Returns all cookies for Supabase to read session data
3. **`setAll()`** - Updates cookies when the session refreshes
4. **try/catch** - Server Components can't set cookies directly; the proxy handles this

## Why Two Clients?

| Environment | Cookie Access     | When Used                                         |
| ----------- | ----------------- | ------------------------------------------------- |
| Browser     | `document.cookie` | Client Components, event handlers                 |
| Server      | `cookies()` API   | Server Components, Server Actions, Route Handlers |

Browser code can't access the `cookies()` API, and server code can't access `document.cookie`. Each client uses the appropriate method for its environment.

## Cookie Flow

Here's how authentication cookies flow through the app:

```
Browser Request
    ↓
proxy.ts (refreshes session, sets cookies)
    ↓
Server Component (reads cookies via server client)
    ↓
HTML Response (includes Set-Cookie headers)
    ↓
Browser (stores cookies, uses browser client)
```

The proxy refreshes expired sessions on every request. Server Components read the current session. The browser stores updated cookies for future requests.

## Troubleshooting

**"cookies is not a function" error:**

You're likely importing from the wrong package. Use:

```typescript
import { cookies } from "next/headers";
```

Not:

```typescript
// Wrong!
import { cookies } from "next/navigation";
```

**"Cannot set cookies in Server Component" warning:**

This is expected. The `setAll` try/catch handles this case. The proxy (`proxy.ts`) handles session refresh instead.

**TypeScript errors about cookie types:**

Make sure you have the latest `@supabase/ssr` version:

```bash
pnpm update @supabase/ssr
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
