Guides

How to Use Supabase Authentication With Next.js

Supabase Auth combined with the App Router requires a specific setup so that session state is available consistently in both server components and client components — this walks through that setup end to end.

Prerequisites

  • arrow_rightA Supabase project (free tier is fine)
  • arrow_rightA Next.js App Router project
  • arrow_right@supabase/supabase-js and @supabase/ssr packages installed

Step 1: Install and Configure

npm install @supabase/supabase-js @supabase/ssr

Add your Supabase project URL and anon key as environment variables (NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY).

Step 2: Create Server and Client Supabase Clients

lib/supabase/server.ts

import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';

export function createClient() {
  const cookieStore = cookies();
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    { cookies: { getAll: () => cookieStore.getAll() } }
  );
}

Step 3: Protect Routes With Middleware

Middleware runs before a request reaches a page, making it the right place to check session validity and redirect unauthenticated users before any page code executes — this is faster and more secure than checking auth state inside the page component.

Common Errors

  • arrow_rightSession appears logged out on the server but logged in on the client — usually a cookie handling mismatch between the server and client Supabase clients
  • arrow_right"Invalid JWT" errors after deploying — confirm environment variables are set correctly in the deployment environment, not just locally
  • arrow_rightRedirect loops on protected routes — check the middleware's matcher config isn't also intercepting the login page itself

Security Considerations

  • arrow_rightNever expose the Supabase service role key to the client — only the anon key belongs in NEXT_PUBLIC_ variables
  • arrow_rightRow-level security policies are the real access control layer — client-side route protection alone isn't sufficient
  • arrow_rightValidate the session independently in every API route, not just in middleware

Frequently Asked Questions

Why do I need both a server and client Supabase instance?add

Server components and client components read the session differently — the server client reads from cookies via Next.js's server APIs, while the client instance manages the browser session; using the wrong one in the wrong context causes session mismatches.

Is middleware required for auth to work?add

Not strictly, but without it, unauthenticated users would reach page code before being redirected, which is both slower and a weaker security boundary than checking in middleware first.

How do I handle OAuth providers like Google?add

Configure the provider in the Supabase dashboard, then use supabase.auth.signInWithOAuth() with a redirect URL pointing to a callback route that exchanges the auth code for a session.

BUILDING SOMETHING SIMILAR?

If you'd rather have this built and shipped than build it yourself, I'm available for freelance and contract work.