better-auth is a framework-agnostic TypeScript authentication library that hit 28k GitHub stars by fixing the exact pain points NextAuth developers complain about: opaque session types, fragile adapters, and a plugin system that fights you on anything non-standard. After porting a production Next.js app from NextAuth v5 to better-auth last month, my verdict is simple — for most new projects, better-auth wins on DX, type safety, and edge-case handling. Here's the side-by-side.
The short version: NextAuth v5 (Auth.js) is still fine if you're deep in the Next.js ecosystem and only need OAuth + a session cookie. The moment you want organisations, magic links that actually work, 2FA, or a session object whose shape TypeScript knows about, better-auth pulls ahead so hard the comparison stops being interesting.
What is better-auth and why did it blow up?
better-auth: a framework-agnostic TypeScript-first auth library that ships sessions, OAuth, email/password, magic links, 2FA, passkeys, and organisations as first-party features rather than community plugins. It runs on Next.js, Remix, SvelteKit, SolidStart, Astro, Hono, Elysia, and plain Node.
The growth curve isn't a mystery. NextAuth has been the default React auth library for years, and its v5 rewrite (now Auth.js) tried to fix the worst parts of v4 — but it kept the same fundamental design: a framework wrapper around providers, with session shapes determined by callbacks and TypeScript types that you have to augment manually via module declaration merging. Anyone who has spent an afternoon Googling declare module "next-auth" knows the pain.
better-auth flips this. You define your auth config once, and the client gets fully-typed sessions, users, and organisations inferred from your schema. No augmentation, no any escape hatches.
How does better-auth setup compare to NextAuth v5?
better-auth setup is roughly half the code of NextAuth v5 for the same feature set, and you don't need a separate adapter package for Drizzle, Prisma, or Kysely — they're built in.
Here's a minimal better-auth server config with email/password and GitHub OAuth:
// lib/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./db";
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg" }),
emailAndPassword: { enabled: true },
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
});
The Next.js route handler is one line:
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth.handler);
Compare that with NextAuth v5, where you need auth.config.ts (edge-safe), auth.ts (full), middleware.ts, the adapter package install, a session callback to shape the session, and a jwt callback to shape the token — plus module augmentation if you want any of it typed. I've migrated three projects and the NextAuth setup takes 60-90 minutes on the first run. better-auth is closer to 20.
The typed session, finally
Call auth.api.getSession() on the server, or authClient.useSession() on the client, and the returned object is fully typed — including any plugin extensions (organisations, custom user fields, 2FA state). No as unknown as MySession. No callbacks to remember to update when the schema changes.
const session = await auth.api.getSession({ headers });
// session.user.role is typed because you added the admin plugin
// session.session.activeOrganizationId is typed because organisations is enabled
Why is better-auth better for edge cases?
The plugin system covers organisations, multi-session, passkeys, magic links, OTP, 2FA, admin roles, and API keys as first-party plugins maintained by the core team — not community packages of varying quality. This is the single biggest practical win.
On NextAuth, anything beyond "sign in with Google" turns into a hunt through GitHub issues. Magic links work but you write the email template and the verification flow yourself. Organisations don't exist — you build them in your data layer and hope the session callback keeps pace. 2FA is a roll-your-own affair. Passkeys are community plugins with single-digit weekly downloads.
better-auth ships these as imports:
import { organization, twoFactor, passkey } from "better-auth/plugins";
export const auth = betterAuth({
// ...
plugins: [organization(), twoFactor(), passkey()],
});
Each plugin adds the necessary database tables, API routes, and client methods. The client picks up the new methods through type inference — you get authClient.organization.create() and authClient.twoFactor.enable() with full IntelliSense.
When should you still pick NextAuth v5?
Pick NextAuth v5 if you're maintaining an existing NextAuth v4 app, or you only need OAuth sign-in with no extra features and want the path most documented on Stack Overflow. The migration cost from v4 to v5 is already paid for many teams, and ripping it out for better-auth is rarely worth it on a working app.
NextAuth still has the bigger community footprint and more tutorial coverage. If you Google a weird OAuth error at 11pm, you'll find the answer faster for NextAuth. better-auth's docs are excellent but the long-tail StackOverflow corpus doesn't exist yet.
It's also worth being honest: better-auth is younger. Major version churn is possible, and the org plugin's API has already shifted once in 2025. If you need rock-solid API stability for a five-year project, that's a real consideration.
For everything else — new projects, side projects, anything you'd build on the 2026 React stack, or any non-Next framework like Remix or SvelteKit — better-auth is the right call. The DX gap is too big to ignore.
Side-by-side feature comparison
better-auth wins on type safety, built-in features, and framework support; NextAuth wins on ecosystem maturity and community size.
- Type-safe sessions out of the box: better-auth ✅ / NextAuth ❌ (manual augmentation required)
- Organisations / multi-tenancy: better-auth ✅ first-party / NextAuth ❌ DIY
- 2FA + Passkeys: better-auth ✅ first-party plugins / NextAuth ⚠️ community plugins
- Magic links with built-in email template: better-auth ✅ / NextAuth ⚠️ provider only, template is yours
- Framework support: better-auth ✅ Next, Remix, SvelteKit, Solid, Astro, Hono, Elysia, Node / NextAuth ✅ Next, SvelteKit, Express, Solid (less mature)
- Database adapters: better-auth ✅ Drizzle, Prisma, Kysely built-in / NextAuth ⚠️ separate packages
- Ecosystem / Stack Overflow answers: NextAuth ✅ / better-auth ⚠️ growing
- Deployment to Vercel / Netlify: both work fine, better-auth handles edge runtime cleanly without the split-config dance
The migration path from NextAuth
The migration is straightforward for greenfield-like apps but tedious if you have a heavily-customised NextAuth setup. better-auth ships a CLI that scaffolds the database schema and a migration guide that maps NextAuth tables to its own.
The pain points I hit:
- NextAuth's
session.user.idis sometimes missing depending on your strategy. better-auth always has it. Update any code that didsession.user.id ?? session.user.email. - OAuth account linking is on by default in better-auth and was off in my NextAuth setup. Decide which behaviour you want before flipping the switch.
- If you used NextAuth's
jwtcallback to stuff custom claims into the token, better-auth'suseradditional fields plus thecustomSessionplugin is the equivalent — cleaner, but a rewrite.
Plan a day for a small app, a week for anything with custom callbacks and a real user table. Wire up monitoring (Sentry catches auth-flow exceptions you'll otherwise miss) before you cut over, not after.
The verdict
Use better-auth for any new TypeScript project where auth is non-trivial — meaning you need typed sessions, organisations, 2FA, or anything past basic OAuth. NextAuth v5 stays in its lane: existing Next.js apps where the auth surface is small and the migration cost outweighs the DX gain.
The 28k stars aren't hype. better-auth is what NextAuth should have been if it had been designed TypeScript-first from day one instead of bolted on. Try it on your next side project — the setup will be done before you'd have finished reading the NextAuth v5 migration guide.
FAQs
Is better-auth production ready in 2026?
Yes — better-auth is used in production by hundreds of teams and the core API has stabilised through 2025. Plugin APIs still shift occasionally, so pin versions and read release notes before upgrading minor versions on production apps.
Does better-auth work with Next.js App Router and Server Actions?
Yes, better-auth has first-party Next.js support including App Router, Server Components, Server Actions, and the edge runtime. You get a single handler export for the catch-all route and a getSession helper that works in any server context without the edge/node split-config that NextAuth v5 requires.
Can I use better-auth without Next.js?
Yes — better-auth is framework-agnostic and ships official integrations for Remix, SvelteKit, SolidStart, Astro, Hono, Elysia, and plain Node HTTP. This is one of its biggest advantages over NextAuth, which despite the Auth.js rebrand still feels Next-first in practice.
How does better-auth handle database migrations?
better-auth's CLI generates SQL or ORM migrations for your chosen adapter (Drizzle, Prisma, Kysely). Run npx @better-auth/cli generate to produce the schema, then apply it through your normal migration workflow — no separate auth-migrations system to learn.
What's the bundle size difference vs NextAuth?
better-auth's client bundle is smaller — around 12kb gzipped for the core client versus NextAuth's roughly 18kb. The server-side footprint is comparable. Bundle size isn't the reason to switch, but it's a small win on top of the DX gains.