How to Add Authentication to a Next.js App (2026)
Every SaaS needs auth. Here are three ways to add authentication to Next.js in 2026, from simplest to most flexible.
Option 1: Clerk (Fastest Setup)
Clerk gives you a complete auth system with pre-built UI components. 5 minutes from zero to working auth.
Step 1: Install
npm install @clerk/nextjs
Step 2: Add Environment Variables
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_xxx
CLERK_SECRET_KEY=sk_xxx
Step 3: Add Middleware
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (isProtectedRoute(req)) {
await auth.protect()
}
})
export const config = {
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
}
Step 4: Add Provider
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }) {
return (
<ClerkProvider>
<html><body>{children}</body></html>
</ClerkProvider>
)
}
Step 5: Add Sign-In UI
// app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from '@clerk/nextjs'
export default function SignInPage() {
return <SignIn />
}
That's it. You have sign-up, sign-in, email verification, OAuth (Google, GitHub), and session management. Clerk handles the UI, email sending, and security.
Pricing: Free up to 10,000 MAU. Pro: $25/month + $0.02/MAU after 10K.
Best for: SaaS apps that want auth done fast with zero maintenance.
Option 2: NextAuth.js (Auth.js) — Open Source
NextAuth (now Auth.js v5) is the open-source standard. More setup than Clerk, but free and flexible.
Step 1: Install
npm install next-auth@beta
Step 2: Configure Auth
// auth.ts
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'
import Credentials from 'next-auth/providers/credentials'
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
})
Step 3: Add API Route
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'
export const { GET, POST } = handlers
Step 4: Add Middleware
// middleware.ts
import { auth } from './auth'
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname.startsWith('/dashboard')) {
return Response.redirect(new URL('/api/auth/signin', req.nextUrl))
}
})
Step 5: Use in Components
// Server component
import { auth } from '@/auth'
export default async function Dashboard() {
const session = await auth()
if (!session) redirect('/api/auth/signin')
return <h1>Welcome, {session.user.name}</h1>
}
// Client component
'use client'
import { useSession } from 'next-auth/react'
export function UserMenu() {
const { data: session } = useSession()
return session ? <p>{session.user.email}</p> : <SignInButton />
}
Pricing: Free and open source. You host the session storage (database adapter needed for production).
Best for: Developers who want full control and don't want to pay for auth.
Option 3: Supabase Auth — Full Backend
Supabase Auth comes free with Supabase. If you're already using Supabase for your database, auth is built in.
Step 1: Install
npm install @supabase/supabase-js @supabase/ssr
Step 2: Create Supabase Client
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
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) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options))
},
},
}
)
}
Step 3: Sign Up / Sign In
const supabase = await createClient()
// Sign up
await supabase.auth.signUp({ email, password })
// Sign in
await supabase.auth.signInWithPassword({ email, password })
// OAuth
await supabase.auth.signInWithOAuth({ provider: 'google' })
// Get user
const { data: { user } } = await supabase.auth.getUser()
Step 4: Middleware
// middleware.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse } from 'next/server'
export async function middleware(request) {
const response = NextResponse.next()
const supabase = createServerClient(url, key, { cookies: { ... } })
const { data: { user } } = await supabase.auth.getUser()
if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return response
}
Pricing: Free up to 50,000 MAU. Included with Supabase plan.
Best for: Apps already using Supabase for database. Row-level security ties auth directly to data access.
Which Should You Choose?
| Clerk | NextAuth | Supabase Auth | |
|---|---|---|---|
| Setup time | 5 min | 30 min | 20 min |
| UI components | ✅ Pre-built | ❌ Build your own | ❌ Build your own |
| Cost | $0-25+/mo | Free | Free (with Supabase) |
| OAuth providers | 20+ | 80+ | 20+ |
| Passwordless | ✅ | ✅ | ✅ |
| MFA | ✅ | Plugin | ✅ |
| User management UI | ✅ Dashboard | ❌ | ✅ Dashboard |
| Organizations/Teams | ✅ | ❌ | ❌ |
| Open source | ❌ | ✅ | ✅ |
Choose Clerk When
- Want auth done in 5 minutes
- Need pre-built UI components
- Need organization/team features
- Budget allows $25+/month
- Don't want to maintain auth infrastructure
Choose NextAuth When
- Want free, open-source auth
- Need maximum OAuth provider support
- Want full control over the auth flow
- Building custom sign-in/sign-up pages
- Don't want vendor lock-in
Choose Supabase Auth When
- Already using Supabase for database
- Want auth + database in one platform
- Need row-level security
- Building a real-time app with Supabase
FAQ
Can I switch auth providers later?
Yes, but it's painful. User sessions, tokens, and database schemas differ between providers. Choose carefully upfront.
Is Clerk worth paying for?
If your time is worth more than $25/month (it is), yes. Clerk saves days of auth implementation and maintenance.
Do I need a database for NextAuth?
For development, no (JWT sessions). For production, yes — use a database adapter (Prisma, Drizzle) for persistent sessions.
What about Lucia Auth?
Lucia was deprecated in 2024. The author recommends building auth yourself or using the libraries above.
Bottom Line
Clerk for fastest setup and best DX. NextAuth for free, open-source, maximum control. Supabase Auth if you're already on Supabase.
For most new SaaS apps in 2026: start with Clerk. The time savings justify the cost. Switch to NextAuth if you need to eliminate the dependency.