Better Auth: The Game‑Changer JS Authentication Has Been Waiting For

Authentication in the JavaScript ecosystem has been a patchwork for years: you either wire up lots of library glue or outsource to a SaaS and give up control. Better Auth flips that script. It’s framework‑agnostic, runs inside your app, keeps data in your DB, and ships a plugin system for advanced features—without per‑user pricing.
This post shows why it’s different and how to get running with copy‑pasteable snippets you can adapt today.
Why Better Auth stands out
- Framework‑agnostic: works with Next.js, Remix, Nuxt, SvelteKit, Express, Hono, Fastify, and more
- Built‑in features: email+password, social SSO, 2FA, passkeys, multi‑session, rate limiting, orgs/roles
- Plugin ecosystem: add features via plugins instead of bespoke code
- Keep your data: users stay in your database; no per‑user costs
- Full control: customize flows, cookies, endpoints, and types, end‑to‑end
Quick start on Next.js: from zero to sign‑in
1) Install
# pick your package manager
pnpm add better-auth
# npm i better-auth / yarn add better-auth / bun add better-auth
2) Create the server instance (email+password + Next server action cookies)
// lib/auth.ts
import { betterAuth } from "better-auth";
import { nextCookies } from "better-auth/next-js";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
// autoSignIn: true by default; set to false to require manual sign-in after sign-up
autoSignIn: false,
},
// Ensure cookies are set automatically in Next.js Server Actions/RSC
plugins: [nextCookies()], // keep this as the last plugin
});
3) Mount the handler (Next.js App Router)
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
// Mount Better Auth under /api/auth/*
export const { GET, POST } = toNextJsHandler(auth.handler);
Client setup (Next.js React client)
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
// baseURL: "http://localhost:3000" // set if your API is on a different origin
});
Simple flows you’ll use daily
Email & Password (Next.js)
Sign up/sign in with Server Actions (cookies auto-set via nextCookies
plugin):
// app/(auth)/actions.ts
"use server";
import { auth } from "@/lib/auth";
export async function signUp(formData: FormData) {
const email = String(formData.get("email"));
const password = String(formData.get("password"));
await auth.api.signUpEmail({
body: { email, password },
});
}
export async function signIn(formData: FormData) {
const email = String(formData.get("email"));
const password = String(formData.get("password"));
await auth.api.signInEmail({
body: { email, password },
});
}
Social login (e.g., GitHub)
Configure provider (server):
// lib/auth.ts
import { betterAuth } from "better-auth";
export const auth = betterAuth({
// ...other options
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
});
Trigger from a Client Component:
// app/sign-in/SignInButtons.tsx
"use client";
import { authClient } from "@/lib/auth-client";
export function SignInButtons() {
return (
<button
onClick={() =>
authClient.signIn.social({
provider: "github",
callbackURL: "/dashboard",
errorCallbackURL: "/error",
newUserCallbackURL: "/welcome",
})
}
>
Continue with GitHub
</button>
);
}
Access session data
Client hook (Client Component):
// app/_components/User.tsx
"use client";
import { authClient } from "@/lib/auth-client";
export function User() {
const { data: session, isPending } = authClient.useSession();
if (isPending) return null;
return session ? (
<span>{session.user.email}</span>
) : (
<button onClick={() => authClient.signOut()}>Sign out</button>
);
}
Server (RSC or route handler):
// app/dashboard/page.tsx (RSC)
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 null; // or redirect("/sign-in")
return <div>Welcome {session.user.email}</div>;
}
Add power with one line: plugins
Two‑Factor Authentication (2FA):
// lib/auth.ts
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
// ...other options
plugins: [twoFactor()],
});
Client usage (React client):
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
import { twoFactorClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [twoFactorClient({ twoFactorPage: "/two-factor" })],
});
Passkeys (WebAuthn, Client Component):
// app/sign-in/PasskeyButton.tsx
"use client";
import { authClient } from "@/lib/auth-client";
export function PasskeyButton() {
return (
<button
onClick={() =>
authClient.signIn.passkey({ email: "john@doe.com", callbackURL: "/dashboard" })
}
>
Sign in with Passkey
</button>
);
}
Anonymous (guest) sign‑in:
await authClient.signIn.anonymous();
API Keys (for server‑to‑server):
// lib/auth.ts
import { apiKey } from "better-auth/plugins";
export const auth = betterAuth({
// ...other options
plugins: [apiKey()],
});
Production tips and gotchas (Next.js)
- API route: mount via
toNextJsHandler(auth.handler)
under/api/auth/[...all]
. - Cookies: secure and httpOnly in production by default; configure
advanced.useSecureCookies
, custom names, and cross‑subdomain cookies as needed. - CORS/Origins: when calling across domains, set
trustedOrigins
on the server and configure CORS in your framework. - Next.js middleware: for fast redirects, check for a session cookie; for secure checks, validate with
auth.api.getSession()
inside routes/actions. - TypeScript: built for strict mode; you can infer user/session types from your
auth
instance.
Why this is a game‑changer
- One mental model across frameworks—move code without rewriting auth.
- Batteries included, yet extensible—most teams can delete custom auth glue.
- Own your users and your costs—no vendor lock‑in, no per‑MAU fees.
If you’re starting a new app or migrating from Auth0/Clerk/NextAuth, Better Auth gives you control, performance, and a simpler developer experience.
References & further reading
- Installation, handlers, and plugins: better-auth.com (see docs for your framework)
- Examples: Next.js, Nuxt, Remix, SvelteKit, Astro
- Core concepts: sessions, cookies, client hooks, API endpoints, plugins
Want a follow‑up? I can publish a step‑by‑step Next.js starter with email+password, GitHub SSO, and 2FA enabled, plus route protection and middleware.