Better Auth
Better Auth is a framework-agnostic, TypeScript-first authentication and authorization library. It runs in Next.js, Nuxt, SvelteKit, Astro, Hono, and 20+ other frameworks, and ships features that traditionally required either a paid service (Clerk, Auth0, WorkOS) or a heavy assembly job: passkeys, 2FA, organizations and teams, magic links, OAuth across 40+ providers, SSO/SAML/SCIM for enterprise, plus a plugin architecture that lets you bolt on the rest.
The headline story for 2026: Auth.js (formerly NextAuth.js) joined forces with Better Auth in late 2025. The Better Auth team now maintains both projects. For new Next.js apps, the recommended starting point has shifted — Better Auth is the modern default, and Auth.js is in long-term-support mode for legacy users.
When to Reach for Better Auth
- You're starting a new Next.js (or any TypeScript) app and want a code-first, self-hosted auth layer that doesn't lock you to a vendor.
- You want passkeys, 2FA, organizations, and SSO without integrating five different libraries.
- You're considering Clerk but balking at the per-MAU pricing or the requirement to use their hosted UI components.
- You're on Auth.js / NextAuth today and want a forward path that the same maintainers are actively building.
- You need a framework-agnostic auth layer because your stack spans Next.js + a separate API service.
What's in the Box
Out of the box, no plugins required:
- Email + password with verification and reset flows
- Social OAuth for 40+ providers (Google, GitHub, Apple, Discord, Microsoft, X, plus regional providers like WeChat)
- Sessions with rotation, automatic CSRF, and immediate revocation
- Rate limiting on auth endpoints by default
- Database migrations auto-generated from your schema
- Type-safe client — every endpoint is typed end-to-end
That last point is the difference from a lot of older libraries. You get autocomplete from useSession() through to the database row, no manual type-juggling.
Plugins (the real superpower)
Plugins extend Better Auth without forking. The official plugin set covers:
| Plugin | Adds |
|---|---|
passkey |
WebAuthn / passkey login with no separate service |
two-factor |
TOTP-based 2FA, backup codes, optional SMS |
organization |
Multi-tenant orgs, team membership, roles, invitations |
magic-link |
Email-link login (no password) |
username |
Username-based identity instead of email-as-id |
phone-number |
SMS-based auth |
oauth-proxy |
Run a single OAuth callback URL across many environments |
admin |
Admin dashboard primitives — list users, ban, impersonate |
api-key |
First-class API key management for your own users |
sso |
SAML and enterprise SSO |
oidc-provider |
Make your app an OIDC provider for other apps |
You import the plugin, add it to the config, and the new endpoints + types light up automatically. There's no separate install per surface (server, client, types).
Next.js App Router Setup
Install:
npm install better-auth
npm install @better-auth/prisma-adapter # or drizzle / kysely / mongo
Define the auth config in lib/auth.ts:
import { betterAuth } from "better-auth";
import { prismaAdapter } from "@better-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
export const auth = betterAuth({
database: prismaAdapter(new PrismaClient(), { provider: "postgresql" }),
emailAndPassword: { enabled: true, requireEmailVerification: true },
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
});
Mount the catch-all handler at app/api/auth/[...all]/route.ts:
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);
Add a typed client in lib/auth-client.ts:
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL,
});
export const { signIn, signUp, signOut, useSession } = authClient;
That's the entire integration — three files, no per-page wiring.
Reading the session in a server component
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
export default async function DashboardPage() {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session) return <SignInPrompt />;
return <Dashboard user={session.user} />;
}
Sessions are server-validated — no client trust, no hydration mismatch.
Sign-up flow on the client
"use client";
import { signUp } from "@/lib/auth-client";
await signUp.email({
email: "user@example.com",
password: "supersecret",
name: "Jane",
});
The endpoint is fully typed, including the response shape. Email verification and rate limiting are on by default.
Database Adapters
Better Auth ships first-party adapters for Prisma, Drizzle, Kysely, MongoDB, and a memory adapter for tests. The schema is defined in code; running npx better-auth migrate generates and applies the SQL or ORM migrations for you.
For a vibe-coding stack, Drizzle on Postgres is the most common combination — pairs well with Supabase, Neon, or Vercel Postgres-via-Marketplace.
Better Auth vs the Alternatives
| Better Auth | Clerk | Auth.js (NextAuth) | Lucia | Supabase Auth | |
|---|---|---|---|---|---|
| Hosted | Self-hosted (free) | Hosted (paid per MAU) | Self-hosted | Self-hosted | Hosted (Supabase) |
| Plugins | Rich official set | Built-in features only | Smaller ecosystem | Bring-your-own | Built-in features only |
| 2FA / passkeys | Yes (plugins) | Yes (built-in) | Limited | Bring-your-own | Yes |
| Orgs / teams | Yes (plugin) | Yes (paid tier) | No | Bring-your-own | Limited |
| SSO / SAML | Yes (plugin) | Yes (Enterprise tier) | No | No | No |
| Lock-in | None | High | None | None | Medium (Supabase platform) |
| Best for | New apps wanting code-first auth | Teams that want hosted UIs | Existing Next.js apps | Power users wanting full control | Apps already on Supabase |
The honest summary: for new Next.js apps in 2026, Better Auth is the strongest default. It gives you the feature parity of Clerk without the per-MAU bill, and the maintenance posture of Auth.js without the legacy baggage.
When NOT to Pick Better Auth
- You want a no-code auth experience with a hosted UI you don't have to style. Clerk wins there.
- You need SOC 2 / HIPAA-compliant managed auth out of the box. WorkOS or Auth0 win there.
- You're on Supabase already and don't have a feature gap. Stay on Supabase Auth — one less service to operate.
- You're maintaining a critical legacy Auth.js codebase and migration risk outweighs feature gains. Sit tight; Auth.js is in safe hands now.
Common Gotchas
- Trusted origins — set
trustedOriginsin your config to include all Vercel preview URLs (*.vercel.app) you actually use, or auth callbacks will fail in preview deploys. - Email verification — enabling
requireEmailVerificationrequires a working email sender (Resend / Loops / Postmark). Pick the sender before turning the flag on. - Session cookie domain — for monorepos with separate front/back deploys, set the cookie domain explicitly. Default is host-only.
- Plugin order matters — some plugins extend the schema (organization, two-factor). Run
better-auth migrateafter adding any new plugin or your queries will fail.
Production Checklist
- Schema migrated via
better-auth migrateand committed - Email + password with verification enabled, sender configured
- At least one social provider configured for low-friction signup
- Trusted origins list includes preview deploys
- Session rotation and CSRF defaults left on (don't disable)
- Rate-limit settings reviewed for your traffic shape
- If you ship a Pro tier:
organizationplugin in place from day one — retrofitting later is painful - If your users handle anything sensitive:
passkeyandtwo-factorplugins enabled - Middleware protecting
app/(auth)/*routes via session check
See Also
- Authentication — the underlying concepts Better Auth implements
- Clerk Billing — the hosted alternative this replaces for many teams
- Supabase Auth — when staying on Supabase makes more sense
- Passkeys — what Better Auth's passkey plugin gives you
- Polar — billing layer that pairs naturally with Better Auth on indie SaaS stacks
- Stripe — when you'd run Better Auth + Stripe instead of Clerk