· auth / nextjs / authjs

Auth.js vs Clerk — DIY vs hosted auth for Next.js apps

Auth.js keeps users in your own database, free at any scale. Clerk bills ~$17,225/month at 1M users but ships login in 15 minutes. The split: 50k MRU.

By Ethan

1,740 words · 9 min read

Auth.js keeps your users in your database and costs nothing at a million users. Clerk bills roughly $17,225 a month at that scale. The other side: Clerk takes 15 minutes to set up, ships pre-built UI components, and has official support for Astro. Auth.js takes at least an hour, ships zero UI, and its v5 is still in beta.

Below 50k monthly active users, Clerk is free and faster to ship. Above that threshold, Auth.js starts looking like the more durable choice. Data sovereignty requirements or strict compliance rules accelerate that decision.

Who this is for

Developers picking auth for a Next.js App Router project who want a concrete answer. If you’re on Astro, the findings apply — the Astro section is specifically relevant to that stack.

What we tested

Auth.js [email protected] against @clerk/nextjs (v6.x era), both on Next.js App Router. Sources are the official documentation for both libraries. No benchmark numbers were fabricated — where instrumented measurement would be required, we say so.

Findings

Setup cost

Clerk wins this category clearly.

Auth.js setup (one OAuth provider, no DB adapter):

  1. npm install next-auth@beta
  2. npx auth secret — generates the required AUTH_SECRET env var
  3. Create auth.ts at the project root: NextAuth({ providers: [] })
  4. Add a route handler at app/api/auth/[...nextauth]/route.ts
  5. Add optional middleware in middleware.ts
  6. Configure client ID and secret for each OAuth provider separately

Realistic time: 30–60 minutes for one working OAuth provider. Add a DB adapter, callbacks, and a session strategy decision, and you’re looking at 1–2 hours.

Clerk setup:

  1. npm install @clerk/nextjs
  2. Set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY — or skip both with Keyless Mode, which auto-generates temporary credentials so you don’t need a Clerk account to get started
  3. Add clerkMiddleware() to middleware.ts
  4. Wrap the root layout with <ClerkProvider>

Realistic time: 5–15 minutes to a working login. Keyless Mode means you can verify the integration works before you’ve created a Clerk account.

The gap is structural, not something better docs could fix. Auth.js is a library you configure; Clerk is a product you connect to.

Supported providers

Auth.js: 130+ pre-configured OAuth providers. GitHub, Google, Apple, Discord, Twitter, Microsoft Entra ID, Okta, Salesforce, Notion, Stripe, LinkedIn, Spotify, and over a hundred more. Email magic links via Resend, SendGrid, and NodeMailer. Credential (password) auth is supported but the docs officially discourage it.

Passkeys: experimental. Auth.js added WebAuthn support in beta.17 behind an experimental: { enableWebAuthn: true } flag. The docs explicitly say this is not recommended for production. You also need a DB adapter, a separate Authenticator table migration, and @simplewebauthn/browser + @simplewebauthn/server as peer dependencies.

SAML: not native. You can proxy through Okta or Azure AD as an OAuth provider. It works, but you’re assembling it yourself.

Clerk: Google, GitHub, Facebook, and 20+ additional social providers (the exact count isn’t publicly listed; you see all options in the Clerk dashboard at signup). Email OTP and magic links. Phone SMS OTP. Web3 wallet auth — MetaMask, Coinbase Wallet, Base, OKX.

Passkeys: production-ready on the Pro plan. No experimental flag, no extra peer dependencies.

SAML/Enterprise SSO: built in. Azure AD, Google Workspace, Okta, and any SAML-compliant IdP — both SP- and IdP-initiated flows (SAML only for IdP-initiated). Available in development on the free plan (up to 25 connections) and in production on Pro.

If enterprise SSO is on your product roadmap for the next six months, the Auth.js path requires DIY infrastructure. If OAuth provider breadth matters — 130+ vs. 20+ — Auth.js wins that count.

Data ownership

This is the most consequential difference for regulated workloads.

Auth.js stores everything in your own database. You choose the adapter: Prisma, Drizzle ORM, MongoDB, Firebase, Neon, Supabase, Vercel Postgres, and more. Zero user data leaves your infrastructure by default. If you’re choosing between Prisma and Drizzle for that adapter, the Prisma vs Drizzle comparison covers the trade-offs in depth. Sessions are either JWT (stateless, no DB roundtrip, ~4KB size limit, can’t revoke before expiry without a blocklist) or database-backed (stateful, revocable, requires the adapter, adds a DB roundtrip per request).

Clerk hosts the user store in Clerk’s cloud. You can sync data to your own DB via webhooks. Clerk is SOC 2 certified (Business plan and above). HIPAA compliance requires the Enterprise plan with a BAA. Clerk doesn’t offer regional data storage — it satisfies EU data transfer rules via the EU–US Data Privacy Framework instead.

For GDPR-sensitive workloads, healthcare, EU fintech, or any context where “user records in a third-party SaaS” is off the table, Auth.js is the only viable option. For teams that want managed compliance without standing up their own security infrastructure, Clerk’s certifications are worth something real.

Pricing

Auth.js is free. MIT license. You pay your database host and email provider.

Clerk is free up to 50,000 Monthly Retained Users (MRU). MRU counts users who return more than 24 hours after signup — first-day signups don’t count (“First Day Free” grace). A one-month grace period also applies before overage billing begins.

Users (MRU)Auth.jsClerk (Pro, $25/mo base, monthly billing)
10k$0$25/mo (within free tier)
100k$0~$1,025/mo
1M$0~$17,225/mo

The overage rate tiers down at scale: $0.020/MRU from 50k–100k, $0.018 from 100k–1M, $0.015 from 1M–10M.

The 50k free tier is generous for most early-stage products. The cost curve past that point is steep enough that teams who anticipate significant scale should factor it in before they’re locked in.

DX: TypeScript, middleware, and the beta question

Auth.js is fully TypeScript-typed, using module augmentation to extend Session, User, Account, and JWT types globally. The callback model — callbacks.jwt, callbacks.session, callbacks.signIn — is verbose but explicit. Once you understand the shape, it’s predictable.

The rough edge is the edge-runtime split config pattern. Auth.js v5’s core is built on standard Web APIs and is edge-compatible. DB adapters are not — TCP socket connections can’t run at the edge. The solution:

  • One auth.ts with no DB adapter for use in middleware.ts — runs at the edge with JWT sessions
  • One auth.ts with a DB adapter for API routes — runs in the Node.js runtime with database sessions

This is architecturally correct, but it’s non-obvious and a recurring stumbling block for developers coming from v4 or new to edge deployments. The docs cover it, but you need to know to look for it.

The larger question is stability. [email protected] is the current npm release. Stable v4 is 4.24.14. v5 is production-deployed across thousands of apps and is the version all current documentation targets — but it carries breaking changes from v4 and has no published ETA for a stable release. If your team has a policy against running pre-stable library versions, note that.

Clerk ships a stable, production-graded SDK. auth() for server components, useAuth() for client hooks, clerkMiddleware() for route protection with simple pattern matching. Full TypeScript. Keyless Mode removes all first-run friction. Nothing in Clerk’s setup requires understanding edge runtime constraints.

UI components

Auth.js ships no UI at all. Login forms, error states, loading skeletons, social login buttons — you build everything. That’s at minimum a day of work for a polished flow; more if you’re building MFA entry, org switching, or magic link expiry handling.

Clerk ships <SignIn />, <SignUp />, <UserButton />, <UserProfile />, and <OrganizationSwitcher /> as drop-in components. You customize color, logo, and routing behavior from the Clerk Dashboard without touching code. They work out of the box on first render.

One thing to note: the free (Hobby) plan shows a “Secured by Clerk” badge on the components. Removing it requires the Pro plan.

Astro support

For Astro projects — toolchew’s own stack — this comparison has a clear answer today.

Auth.js has no official Astro adapter. There is an open pull request, but it hasn’t merged. The Auth.js docs list Astro under “Community integrations,” which means no official support, no guaranteed maintenance, and you own any breakage.

Clerk ships an official @clerk/astro package with a complete quickstart. It requires SSR mode (output: "server" in astro.config.mjs, using the @astrojs/node adapter — static site export isn’t supported). All five components work in Astro. Keyless Mode works there too.

If Astro is your framework and you need auth now, Clerk is the only option with production-grade official support. If you’re still weighing Astro against Next.js, see Next.js vs Astro for the full framework comparison.

Verdict

Pick Auth.js if:

  • You expect to cross 50k MRU and can’t absorb Clerk’s overage cost at scale
  • Data sovereignty is a hard requirement — GDPR, healthcare, EU fintech, or any context where user records can’t live in a third-party SaaS
  • You need the breadth of 130+ OAuth providers and your specific providers aren’t in Clerk’s list
  • You’re comfortable building your own auth UI and managing a DB adapter

Pick Clerk if:

  • You need to ship auth in an afternoon
  • You’re building on Astro and want official support
  • Enterprise SSO or SAML is on your roadmap and you don’t want to wire it yourself
  • You want pre-built, customizable UI components
  • Production passkeys matter now rather than later

The decision point is around 50k MRU. Below it, Clerk is free and faster in every dimension that matters for early-stage shipping. Above it, the cost difference and data ownership trade-offs start justifying Auth.js’s setup overhead and the missing UI layer.

Caveats

Auth.js v5 is still in beta. It’s production-used, but it’s a pre-stable library. If your organization requires stable releases, pin to Auth.js v4 (4.24.14) — understanding that v4 is in maintenance mode and the docs focus has shifted to v5 — or use Clerk.

No public benchmarks compare Auth.js and Clerk on request latency or throughput. Both are fast enough that auth-specific latency isn’t the deciding factor for typical web workloads. We didn’t fabricate numbers.

Clerk’s exact social provider count isn’t documented publicly. “20+” is based on the docs’ language. If a specific less-common provider is a hard requirement, verify it in the Clerk dashboard before committing.

References

  1. Auth.js Getting Started
  2. Auth.js Installation (Next.js)
  3. Auth.js Provider List
  4. Auth.js Session Strategies
  5. Auth.js Passkeys/WebAuthn
  6. Auth.js Edge Compatibility
  7. Auth.js TypeScript Guide
  8. Auth.js Framework Integrations
  9. Auth.js GitHub
  10. Clerk Pricing
  11. Clerk Next.js Quickstart
  12. Clerk Astro Quickstart
  13. Clerk Enterprise SSO/SAML