6 min read

Better Auth vs Auth.js for Next.js 16: Which One to Use in 2026

Mehdi Rezaei
Mehdi
Author
Engineering
Software
Technology

For a new Next.js 16 app in 2026, pick Better Auth. For an existing app that runs well on Auth.js (NextAuth.js), stay put, patch it, and plan the move only when you need a feature Auth.js never shipped. Both projects agree on this. Since September 2025 the Better Auth team maintains Auth.js, and its announcement tells new users to start on Better Auth.

The harder question is what the switch costs you, and when Auth.js is still the better tool. Here is how I decide on client projects.

What changed: Auth.js is now a maintenance project

In September 2025 Better Auth took over Auth.js. Balázs Orbán, the lead maintainer, wrote in the GitHub announcement that the pace had slowed and the scope had outgrown what the old team could support. Existing apps keep getting security and urgent fixes. New features go to Better Auth. In July 2026 Better Auth joined Vercel, which makes the long-term owner of both libraries the same company that ships Next.js.

The release tags tell the same story. On npm today, next-auth@latest is 4.24.15, and v5 is still on the beta tag at 5.0.0-beta.32. Better Auth is on 1.7.14. If you started a project on "NextAuth v5" two years ago, you are still running a beta.

Maintenance mode is not abandonment. The July 2026 security update closed four advisories in one cycle, including a high-severity email check that ran before Unicode normalization and a v5 middleware check that failed open on provider configuration errors. Patches ship, but you have to install them, and pnpm add next-auth@latest gives v5 users the v4 line, so v5 projects must install with the beta tag.

How the two libraries think about auth

Auth.js started as an OAuth helper. You list providers, pick a session strategy (JWT cookie or database), and use callbacks to shape the token. Email and password login exists through the Credentials provider, but the docs push you away from it and you write the password hashing, rate limiting, and reset flow yourself.

Better Auth starts from the user table. Better Auth ships email and password, email verification, and password reset in the core. Everything else is a plugin: two-factor, passkeys, magic links, organizations with members and roles, admin tools, and API keys. The server instance exposes typed methods such as auth.api.getSession, and the client from better-auth/react mirrors them with authClient.useSession().

In practice that means fewer files. On Auth.js, a B2B app with teams and invites grows its own organization tables, invite tokens, and role checks. On Better Auth, the organization plugin brings the schema and the endpoints, and you write the UI.

Setup on Next.js 16

Better Auth needs four pieces:

  1. Install better-auth and set BETTER_AUTH_SECRET (at least 32 characters) and BETTER_AUTH_URL.
  2. Create lib/auth.ts with betterAuth({ ... }), passing a pg Pool or the Drizzle or Prisma adapter.
  3. Run npx auth@latest generate to produce the schema for your ORM, or npx auth@latest migrate if you use the built-in Kysely adapter.
  4. Mount the handler in app/api/auth/[...all]/route.ts with toNextJsHandler(auth).

Auth.js v5 is shorter on day one: one auth.ts that exports handlers, auth, signIn, and signOut, plus the route file. With JWT sessions you need no database at all, and for a side project with GitHub login that is still the fastest path.

If you use Drizzle with Postgres, Better Auth's generated schema drops into your existing migrations. I compared the two ORMs in Drizzle vs Prisma for the App Router, and either works with its adapter.

proxy.ts is a redirect, not a security check

Next.js 16 renamed middleware.ts to proxy.ts, and the proxy now runs on the Node.js runtime by default. That tempts people to do the full session lookup there. Both libraries and the Next.js docs say the same thing: use the proxy for an optimistic redirect, then check the session again in the page, route handler, or server action that reads the data.

With Better Auth the proxy check is cheap: getSessionCookie(request) from better-auth/cookies only tests for the cookie. The real check is await auth.api.getSession({ headers: await headers() }) inside the server component. The July Auth.js advisory about middleware failing open is a good reminder of why. If the only gate sits in one file in front of the app, one bug in that file opens everything behind it. The same rule applies to data fetching, which I covered in Server Components did not replace TanStack Query.

When Auth.js is still the right call

  • The app already works on Auth.js, uses OAuth only, and has no roadmap item for 2FA, passkeys, or organizations. Patch to 4.24.15 or 5.0.0-beta.32 and move on.
  • You need a provider that Better Auth does not cover yet. Auth.js ships dozens of OAuth providers; Better Auth covers the major ones and has a generic OAuth plugin for the rest, so check yours before you plan the move.
  • You run SvelteKit or Express next to Next.js on the shared @auth/core and the team wants one library everywhere. Better Auth supports those frameworks too, but a migration across three apps is a project, not a ticket.

Stateless sessions used to be the main reason to stay. The Better Auth migration guide now says it supports stateless session management without a database, so that gap is closing, though most plugins still need tables.

What the migration costs

The Better Auth migration guide is honest that the two schemas differ. The parts that bite:

  • emailVerified is a boolean in Better Auth and a timestamp in Auth.js.
  • Account fields move to camelCase (refreshToken, not refresh_token), and provider becomes providerId.
  • VerificationToken becomes Verification, with a single id primary key.
  • Passwords live in the account table. Every credential user needs a row with providerId: "credential", or their logins fail after the switch.

On a real app, I plan the work in this order: write a SQL migration that copies users and accounts into the new tables, run both libraries against a staging copy, move the sign-in pages, then swap every auth() call for auth.api.getSession. Expect to log every user out once unless you write a session bridge. For most apps a forced re-login is cheaper than that bridge.

My decision rule

  • New Next.js 16 app, any login method beyond OAuth, or B2B teams: Better Auth.
  • Existing Auth.js app, OAuth only, stable: stay, patch, and add npm audit to CI.
  • Existing Auth.js app that needs 2FA, passkeys, or organizations next quarter: migrate before you build those features, not after.
  • You want someone else to run auth, with hosted UI and support contracts: neither. Look at a hosted service like Clerk and accept the per-user bill.

Auth is the part of a codebase nobody wants to touch twice. If you are cleaning up an older Next.js app, this is one of the things a consultant should fix before writing features, because every later feature reads the session.

Share this article