Skip to main content

Auth.js v5 vs Lucia v3 vs Better Auth 2026

·StarterPick Team
authauthjsluciabetter-authnextjssaas-boilerplate2026

Auth Is Not Solved — It Is Just Complicated Differently

Authentication in Next.js in 2026 means choosing between three competing philosophies: Auth.js (the established standard), Lucia (the "bring your own database" approach), and Better Auth (the comprehensive newcomer). Each makes different trade-offs.

The right choice depends on your stack, your complexity tolerance, and what you are building.

TL;DR

  • Auth.js v5 (NextAuth): Choose for OAuth-first apps (Google, GitHub login), rapid prototypes, and teams that know it. Established, broad adapter support.
  • Lucia v3: Choose for full control over session management and database schema. Best for apps that need custom auth flows.
  • Better Auth: Choose for feature-complete auth with organizations, 2FA, and magic links out of the box. The rising standard.
  • Clerk: Ignore for self-hosted; use for managed auth when you want to pay to not think about auth ($25+/mo).

Key Takeaways

  • Auth.js v5 is the most used (200K+ weekly npm installs) but v5 was a breaking rewrite
  • Lucia was deprecated in September 2024 as a library — the author now recommends it as a "learning resource" and advocates for the patterns, not the package
  • Better Auth launched in 2024 and has grown rapidly (~50K weekly downloads); the most feature-complete self-hosted option
  • All three support Next.js App Router server components and server actions
  • Better Auth is the current recommendation for new projects in 2026

Auth.js v5 (NextAuth)

Auth.js v5 is the fifth major version of NextAuth.js. It introduced edge compatibility, server actions support, and a redesigned API.

Setup

npm install next-auth@beta @auth/prisma-adapter
// auth.ts — v5 configuration:
import NextAuth from 'next-auth';
import { PrismaAdapter } from '@auth/prisma-adapter';
import Google from 'next-auth/providers/google';
import Resend from 'next-auth/providers/resend';
import { db } from './lib/db';

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(db),
  providers: [
    Google({
      clientId: process.env.AUTH_GOOGLE_ID!,
      clientSecret: process.env.AUTH_GOOGLE_SECRET!,
    }),
    Resend({
      from: 'noreply@yourapp.com',
    }),
  ],
  callbacks: {
    session({ session, user }) {
      session.user.id = user.id;
      session.user.role = user.role;
      return session;
    },
  },
});

// app/api/auth/[...nextauth]/route.ts:
export { handlers as GET, handlers as POST };
// Using auth in server components:
import { auth } from '@/auth';

export default async function DashboardPage() {
  const session = await auth();
  if (!session) redirect('/login');

  return <div>Hello, {session.user.name}</div>;
}

// Using auth in server actions:
export async function updateProfile(data: FormData) {
  'use server';
  const session = await auth();
  if (!session) throw new Error('Unauthorized');

  await db.user.update({
    where: { id: session.user.id },
    data: { name: data.get('name') as string },
  });
}

Auth.js v5 Strengths

  • 20+ built-in OAuth providers (Google, GitHub, Discord, Apple, etc.)
  • Active community and extensive documentation
  • Prisma, Drizzle, MongoDB adapters maintained
  • Magic links (email) built-in
  • JWTs or database sessions

Auth.js v5 Weaknesses

  • v5 is still in beta (as of March 2026) — API may change
  • No built-in organization/team support
  • No built-in 2FA
  • Session refresh logic can be tricky

Lucia v3 (Reference Implementation)

Lucia v3 is no longer recommended as a production library. In September 2024, the author deprecated it and shifted to providing auth as educational patterns. The Lucia approach is still valid as architecture guidance.

The Lucia pattern: implement sessions yourself using the concepts Lucia pioneered.

// The "Lucia pattern" — manual session management:
import { sha256 } from '@oslojs/crypto/sha2';
import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding';
import { db } from './db';

export function generateSessionToken(): string {
  const bytes = new Uint8Array(20);
  crypto.getRandomValues(bytes);
  return encodeBase32LowerCaseNoPadding(bytes);
}

export async function createSession(token: string, userId: string) {
  const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
  const session = {
    id: sessionId,
    userId,
    expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),  // 30 days
  };
  await db.session.create({ data: session });
  return session;
}

export async function validateSessionToken(token: string) {
  const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
  const session = await db.session.findUnique({
    where: { id: sessionId },
    include: { user: true },
  });

  if (!session || Date.now() >= session.expiresAt.getTime()) {
    if (session) await db.session.delete({ where: { id: sessionId } });
    return { session: null, user: null };
  }

  // Refresh session if expiring in less than 15 days:
  if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) {
    session.expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30);
    await db.session.update({
      where: { id: sessionId },
      data: { expiresAt: session.expiresAt },
    });
  }

  return { session, user: session.user };
}

Use the Lucia patterns if: You want full control and understand auth deeply. Not recommended for teams who want to ship fast.


Better Auth launched in 2024 as a comprehensive auth solution with features that others charge for or lack entirely.

npm install better-auth
// auth.ts — Better Auth configuration:
import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { organization } from 'better-auth/plugins';
import { twoFactor } from 'better-auth/plugins';
import { db } from './lib/db';

export const auth = betterAuth({
  database: prismaAdapter(db, { provider: 'postgresql' }),
  emailAndPassword: { enabled: true },
  socialProviders: {
    google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET! },
    github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET! },
  },
  plugins: [
    organization(),   // Teams, roles, invitations
    twoFactor(),      // TOTP 2FA
  ],
});

// Route handler:
// app/api/auth/[...all]/route.ts:
import { auth } from '@/auth';
import { toNextJsHandler } from 'better-auth/next-js';
export const { GET, POST } = toNextJsHandler(auth);
// Client-side usage:
import { createAuthClient } from 'better-auth/react';
import { organizationClient } from 'better-auth/client/plugins';
import { twoFactorClient } from 'better-auth/client/plugins';

export const { signIn, signOut, useSession, organization } = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_URL!,
  plugins: [organizationClient(), twoFactorClient()],
});

// In a React component:
const { data: session } = useSession();

// Sign in with organization support:
await signIn.social({ provider: 'google', callbackURL: '/dashboard' });

// Create an organization:
await organization.create({ name: 'Acme Corp', slug: 'acme' });

Better Auth Strengths

  • Organizations/teams built-in — roles, permissions, invitations
  • Two-factor authentication (TOTP) built-in
  • Magic links built-in
  • Passkeys support
  • Rate limiting on auth endpoints
  • Active development with weekly releases
  • TypeScript-first with excellent type inference

Better Auth Weaknesses

  • Newer library — smaller community than Auth.js
  • Documentation still maturing
  • Fewer examples in the wild

Feature Comparison

FeatureAuth.js v5Lucia PatternBetter Auth
OAuth providers20+ built-inManual10+ built-in
Email/passwordVia providerManualBuilt-in
Magic linksVia Resend providerManualBuilt-in
Organizations/teamsNoManualBuilt-in plugin
Two-factor authNoManualBuilt-in plugin
PasskeysNoManualBuilt-in
Session handlingAutomaticManualAutomatic
Drizzle ORMYesYesYes
Prisma ORMYesYesYes
Edge compatibleYesYesYes
Weekly downloads~200KDeprecated~50K

Which Boilerplates Use Which?

BoilerplateDefault Auth
ShipFastAuth.js (NextAuth)
MakerkitBetter Auth or Supabase Auth
SupastarterSupabase Auth
SaaSBoldAuth.js
OpenSaaSWasp auth (Passport.js)
T3 StackAuth.js or NextAuth
Midday v1Supabase Auth

Recommendation

For new projects in 2026: Better Auth.

It provides the most features without sacrificing developer experience. Organizations, 2FA, and magic links are common SaaS requirements. Better Auth includes all three. Auth.js is still a solid choice if your team knows it well, but for new projects, Better Auth is the better starting point.

For existing projects: Keep Auth.js unless you need organizations or 2FA. Migration from Auth.js to Better Auth is non-trivial.

For maximum control: Use the Lucia patterns (implement sessions yourself using the published algorithms) if you need deep customization.

Methodology

Based on publicly available documentation from Auth.js, Lucia, and Better Auth, npm download statistics, and community discussions as of March 2026.


Choosing an auth strategy for your SaaS? StarterPick helps you find boilerplates pre-configured with the right auth library for your needs.

Comments