Authentication in Next.js: NextAuth vs Clerk vs Supabase Auth
Authentication is one of the first decisions you make in a new project and one of the hardest to change later. Here's how the three leading options compare in a production Next.js context.
Why Auth Choice Matters More Than You Think
Authentication touches every layer of your stack: middleware, server components, API routes, client state. Choosing the wrong solution means either ripping it out six months in, or living with architectural debt that makes every new feature harder.
We've shipped production apps with all three of the major Next.js auth solutions. Here's what actually matters.
NextAuth.js (Auth.js v5)
Best for: Custom auth flows, multiple OAuth providers, self-hosted requirements.
NextAuth v5 (now branded Auth.js) was rebuilt for the App Router. The API is cleaner than v4 and integrates naturally with Server Components and middleware.
// auth.ts
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [GitHub],
callbacks: {
authorized({ auth, request }) {
return !!auth?.user; // Protect routes in middleware
},
},
});
// In a Server Component
import { auth } from '@/auth';
export default async function Dashboard() {
const session = await auth();
if (!session) redirect('/login');
return <DashboardContent user={session.user} />;
}
Strengths:
- Free and self-hosted — no per-MAU costs
- Highly configurable: custom session strategies, JWT handling, database adapters
- 40+ OAuth providers built in
- Strong community, mature codebase
Weaknesses:
- You manage the complexity: email templates, password reset flows, MFA all require custom implementation
- Database setup required for session persistence
- No built-in UI components — you build your own sign-in pages
- Debugging session issues can be painful
Verdict: Best for teams comfortable with backend work who want zero vendor lock-in and full control.
Clerk
Best for: Rapid development, teams that want auth solved completely.
Clerk provides hosted authentication with pre-built UI components, a full user management dashboard, and an SDK that integrates deeply with Next.js App Router patterns.
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);
export default clerkMiddleware((auth, req) => {
if (isProtectedRoute(req)) auth().protect();
});
// In a Server Component
import { currentUser } from '@clerk/nextjs/server';
export default async function Profile() {
const user = await currentUser();
return <div>Hello {user?.firstName}</div>;
}
Strengths:
- Complete solution: sign-up, sign-in, MFA, SSO, user profiles, org management
- Pre-built, customizable UI components that look professional out of the box
- Excellent Next.js middleware integration — route protection in 5 lines
- Works edge-side in middleware without database queries
- Webhooks for user events
Weaknesses:
- Costs money at scale ($0.02/MAU after free tier)
- Less control over session logic
- Your users' auth data lives on Clerk's infrastructure
- Customization beyond their theming system requires workarounds
Verdict: Best for SaaS products where shipping fast matters more than minimizing vendor dependencies. The time saved on auth is significant.
Supabase Auth
Best for: Teams already using Supabase for their database.
Supabase Auth is built on GoTrue and integrates tightly with Supabase's Row Level Security (RLS) system. If you're using Supabase as your database, using anything else for auth means giving up the RLS integration — which is a significant loss.
// Create a Supabase server client
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() } }
);
}
// In a Server Component
export default async function Page() {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect('/login');
// RLS automatically filters data to the current user
const { data: posts } = await supabase.from('posts').select();
return <PostList posts={posts} />;
}
Strengths:
- RLS integration is genuinely powerful — database-level security without custom query filters
- Free tier is generous
- Built-in email/password, OAuth, magic links, phone auth
- Self-hostable with Docker
Weaknesses:
- Cookie management with App Router requires careful setup (the
@supabase/ssrpackage) - Less polished middleware integration than Clerk
- Organization/team management not built in
- Weaker TypeScript DX than the alternatives
Verdict: The obvious choice if Supabase is your database. Otherwise, only worth it if you specifically want self-hosted and free.
The Decision Matrix
| Criteria | NextAuth | Clerk | Supabase Auth |
|---|---|---|---|
| Cost at scale | Free | $0.02/MAU | Free/self-hosted |
| Setup time | Days | Hours | Hours |
| Customization | Maximum | Moderate | Moderate |
| Built-in UI | None | Full | Basic |
| MFA | DIY | Built-in | Built-in |
| Org management | DIY | Built-in | Limited |
| Database integration | Via adapters | Webhooks | Native (Supabase) |
| Self-hosted | Yes | No | Yes |
Our Recommendation
- Bootstrapped SaaS / side project: Clerk — ship faster, worry about it later
- Enterprise / compliance requirements: NextAuth with a custom database adapter
- Using Supabase database: Supabase Auth, no question
- High scale, cost-sensitive: NextAuth or self-hosted Supabase
The wrong choice isn't catastrophic. Migrating auth is painful but not impossible. But getting it right upfront saves you a week of migration work at the worst possible moment — usually right before a launch.
Continue reading
Related articles
Rate Limiting in Next.js: Protecting Your API Routes
How to implement production-grade rate limiting in Next.js — with Middleware-level protection, per-user limits, and distributed rate limiting using Upstash Redis.
EngineeringNext.js Parallel Routes and Intercepting Routes: A Complete Guide
Parallel routes and intercepting routes are among the most powerful App Router primitives. This guide explains what they do, when to use them, and how to avoid the common pitfalls.
EngineeringVercel vs Netlify vs AWS Amplify for Next.js in 2026
A practical comparison of the three most common Next.js hosting platforms — Vercel, Netlify, and AWS Amplify — with real cost and capability trade-offs.
Stay informed
Get our monthly deep dives.
Engineering, design, and growth insights — once a month. No spam.
Browse all resources