Skip to main content

Clerk vs NextAuth in 2026: Managed vs Self-Hosted Auth

·PkgPulse Team

TL;DR

Clerk if you want to ship auth in 30 minutes; NextAuth if you need full control or can't justify the cost. Clerk (~1M weekly downloads) is a managed auth service with beautiful pre-built UI, social sign-in, MFA, and organizations out of the box. NextAuth/Auth.js (~2.5M downloads) is free, self-hosted, and highly customizable. Clerk's pricing starts free but scales to $25/month for 10,000 MAU. NextAuth is always free.

Key Takeaways

  • NextAuth/Auth.js: ~2.5M weekly downloads — Clerk: ~1M (npm, March 2026)
  • Clerk is fully managed — no database tables needed for auth
  • Clerk pricing: free → $25+/month at scale — NextAuth is always free
  • Clerk has built-in UI — embeddable sign-in/sign-up components
  • NextAuth supports 80+ OAuth providers — Clerk supports major ones

Setup Comparison

// NextAuth (Auth.js) — self-hosted, needs database adapter
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import { DrizzleAdapter } from '@auth/drizzle-adapter';
import { db } from '@/db';

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: DrizzleAdapter(db), // Stores sessions in your database
  providers: [
    GitHub({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    session({ session, user }) {
      session.user.id = user.id; // Add user ID to session
      return session;
    },
  },
});

// You must:
// 1. Set up database tables (via adapter migration)
// 2. Configure OAuth apps in GitHub/Google
// 3. Handle session state yourself
// Clerk — minimal setup, managed service
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs';

export default function RootLayout({ children }) {
  return (
    <ClerkProvider>
      <html><body>{children}</body></html>
    </ClerkProvider>
  );
}

// middleware.ts — protect routes
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtected = createRouteMatcher(['/dashboard(.*)']);

export default clerkMiddleware((auth, req) => {
  if (isProtected(req)) auth().protect();
});

// That's it — Clerk manages users, sessions, and auth state
// No database setup required for auth

UI Components

// Clerk — beautiful pre-built components (Drop-in UI)
import { SignIn, SignUp, UserButton, UserProfile } from '@clerk/nextjs';

// Full sign-in page in one component:
export default function SignInPage() {
  return <SignIn />;
  // Includes: email/password, social login, MFA prompt
  // Fully styled, customizable via Clerk dashboard theme
}

// User avatar + dropdown (profile, settings, sign out):
function Nav() {
  return (
    <nav>
      <Logo />
      <UserButton afterSignOutUrl="/" />
    </nav>
  );
}
// NextAuth — you build the UI yourself
'use client';
import { signIn, signOut, useSession } from 'next-auth/react';

function SignInPage() {
  return (
    <div>
      <button onClick={() => signIn('github')}>Sign in with GitHub</button>
      <button onClick={() => signIn('google')}>Sign in with Google</button>
      {/* Build your own UI */}
    </div>
  );
}

function Nav() {
  const { data: session } = useSession();
  return (
    <nav>
      <Logo />
      {session ? (
        <button onClick={() => signOut()}>Sign out ({session.user?.name})</button>
      ) : (
        <button onClick={() => signIn()}>Sign in</button>
      )}
    </nav>
  );
}

Organizations / Multi-Tenancy

// Clerk — organizations built in
import { useOrganization, useOrganizationList } from '@clerk/nextjs';

function OrgSwitcher() {
  const { organization } = useOrganization();
  const { setActive, organizationList } = useOrganizationList();

  return (
    <select onChange={(e) => setActive({ organization: e.target.value })}>
      {organizationList?.map(({ organization: org }) => (
        <option key={org.id} value={org.id}>{org.name}</option>
      ))}
    </select>
  );
}

// Clerk handles: invitations, member roles, org switching
// Cost: requires Pro plan ($25+/month)
// NextAuth — implement organizations yourself
// NextAuth has no concept of organizations — build it from scratch:
// - organizations table in your database
// - membership table with roles
// - org context in session
// - invitation system
// Typically 2-4 weeks of work

Pricing Reality

Clerk pricing (March 2026):
  Free: 10,000 MAU
  Pro: $25/month base + $0.02 per MAU over 10K
  B2B (Organizations): included in Pro

  For 50,000 MAU: $25 + (40,000 × $0.02) = $825/month

NextAuth (Auth.js):
  Always free — you pay for your own database hosting
  For 50,000 MAU: $0 (+ whatever your Vercel/PlanetScale costs)

At scale, Clerk's cost advantage disappears. At startup scale (<10K MAU), it's free.


When to Choose

Choose Clerk when:

  • Speed to ship is paramount (auth in 30 minutes)
  • You need organizations/multi-tenancy without building from scratch
  • Your app needs MFA, device sessions, or enterprise SSO
  • <10,000 MAU (free tier)
  • You value the hosted management dashboard

Choose NextAuth/Auth.js when:

  • Cost control is important at scale
  • You need full control over the auth flow
  • Custom auth logic that Clerk's managed service can't accommodate
  • GDPR/compliance requirements that prevent sending user data to third parties
  • 10,000 MAU and cost is a concern


Compare Clerk and NextAuth package health on PkgPulse.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.