Summer sale!-$100 off
home

Better Auth UI: the complete Next.js + shadcn guide (2026)

ยท18 min read

What Better Auth UI is, how to install it with shadcn, wire AuthProvider in Next.js, protect routes, and when a full SaaS starter beats DIY auth screens.

If you searched for Better Auth UI, you are usually past the "which auth library?" debate. You already want Better Auth for sessions, OAuth, and plugins - and you do not want to hand-build every sign-in, sign-up, reset-password, and settings screen from scratch.

Short answer

Better Auth UI is an open-source, MIT-licensed set of ready-made authentication components built specifically for Better Auth. The recommended path today is the shadcn/ui registry (npx shadcn@latest add https://better-auth-ui.com/r/auth.json), with HeroUI and SolidJS variants also available. You own the generated code, style it like any shadcn component, and plug it into your Better Auth client. If you also need billing, organizations, admin, and multi-platform apps - not just auth screens - TurboStarter ships production auth views on Better Auth without assembling the rest of the SaaS stack yourself.

Key takeaways

  1. Better Auth = auth server. Better Auth UI = the screens.
  2. Prefer the 2026 shadcn registry + AuthProvider over legacy @daveyplate/better-auth-ui / AuthUIProvider tutorials.
  3. Protect dashboards with server ensureSession plus client useAuthenticate.
  4. Match server plugins to AuthProvider plugins or buttons will appear broken.
  5. Use a SaaS starter when auth is one of many blockers, not the only one.

This guide covers what Better Auth UI is, how it differs from Better Auth itself, how to install and integrate it in Next.js App Router, which components and plugins matter in production, and when a starter kit is the better use of your time.

What is Better Auth UI?

Better Auth UI is a UI layer for Better Auth: drop-in React (and Solid) components for sign-in, sign-up, password recovery, account settings, user menus, email templates, and plugin-backed flows like magic links, passkeys, organizations, and API keys. It is maintained at better-auth-ui.com with source on GitHub.

The library is Better Auth native. It is not a generic auth kit bolted onto Clerk, Auth.js, or Supabase Auth. That focus is why people search "better auth ui" and "better auth shadcn": they want components that already speak Better Auth's client API.

Three UI stacks ship from the same project. Shared React data hooks live under @better-auth-ui/react (queries, mutations, ensureSession, useAuthenticate), so UI variants share one auth data layer.

Better Auth vs Better Auth UI (do not confuse them)

Search results mix these two names. Treat them as a backend + UI pair: Better Auth owns sessions and plugins; Better Auth UI owns forms, settings, and account chrome. You still configure secrets, schema, email, and OAuth yourself (or inherit them from a starter).

PieceRoleYou still own
Better AuthAuth server, sessions, plugins, database adapters, API routesSecrets, schema, email delivery, OAuth apps, security policy
Better Auth UIForms, settings cards, user button, email templates, route viewsLayout, branding, which plugins are enabled in the provider

Better Auth alone does not give you finished screens. Better Auth UI alone does not replace a configured auth instance, database, or email provider. You need both - or a starter that already wires both.

Legacy package alert

The older npm package @daveyplate/better-auth-ui is still widely referenced in tutorials and DeepWiki pages. It is the legacy distribution. Current docs steer new projects to the shadcn registry and @better-auth-ui/* packages. If you find AuthUIProvider examples online, map them to the newer AuthProvider pattern in the official Next.js guide.

Who should use Better Auth UI?

Use Better Auth UI when you already chose Better Auth and need production auth screens fast on shadcn or HeroUI. Skip it when auth is the product (bespoke identity UI), you are on another auth vendor, or you also need billing, orgs, admin, and mobile this month.

Use Better Auth UI if

  • You chose Better Auth and want days, not weeks, of form work gone
  • Your design system is shadcn/ui (or HeroUI) and you edit components in-repo
  • You need credentials, social login, forgot/reset password, account + security settings
  • You plan Better Auth plugins (magic link, passkey, organization) with matching UI

Skip or use a starter if

  • Every auth pixel is unique brand IP
  • You are not on Better Auth (use that vendor's UI)
  • You need auth + billing + orgs + admin + mobile tomorrow
  • A SaaS starter kit is faster than composing libraries

Prerequisites before you install Better Auth UI

Official docs assume these are already working. Without a live Better Auth server, email, and OAuth apps, polished forms still fail in production.

Next.js (or TanStack Start) + Tailwind - App Router is the path covered below.

shadcn/ui initialized - required for the shadcn registry install path.

Better Auth working - auth server + authClient that can already sign in via API.

Email delivery - verification, magic links, and password reset need a provider in staging.

OAuth apps - create Google/GitHub (or others) with correct redirect URIs before enabling social buttons.

Prove auth before UI

Auth UI cannot fix a missing Resend key or a wrong Google redirect URI. Call signIn.email against your API first, then install components.

How to install Better Auth UI (shadcn path)

The fastest path for most Next.js teams is the registry install. Use the smaller registries for a thin first commit; use all.json when you want plugins and email templates immediately.

npx shadcn@latest add https://better-auth-ui.com/r/auth.json

Pulls the dynamic Auth component and views for sign-in, sign-up, forgot-password, reset-password, and sign-out.

HeroUI users install packages instead of the registry - see HeroUI quick start. Always prefer the current docs over older @daveyplate/better-auth-ui READMEs when APIs diverge.

Next.js App Router setup (AuthProvider + routes)

The official Next.js integration follows one pattern: shared React Query client โ†’ AuthProvider โ†’ dynamic /auth/[path] and /settings/[path] routes โ†’ session guards. After wiring, your tree looks like this:

layout.tsx
auth.tsx
auth-provider.tsx
providers.tsx
auth.ts
auth-client.ts
query-client.ts

Step-by-step wiring

Create a factory that returns a new client per server request and a singleton in the browser so caches never leak across users:

lib/query-client.ts
import { QueryClient } from "@tanstack/react-query";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 5_000,
      },
    },
  });
}

let browserQueryClient: QueryClient | undefined;

export function getQueryClient() {
  if (typeof window === "undefined") {
    return makeQueryClient();
  }

  browserQueryClient ??= makeQueryClient();
  return browserQueryClient;
}

Follow the official example closely if your Better Auth UI version expects a TanStack helper for environment detection. The important rule is no shared QueryClient across requests on the server.

components/providers.tsx
"use client";

import { QueryClientProvider } from "@tanstack/react-query";
import Link from "next/link";
import { useRouter } from "next/navigation";
import type { ReactNode } from "react";

import { authClient } from "@/lib/auth-client";
import { getQueryClient } from "@/lib/query-client";
import { AuthProvider } from "@/components/auth/auth-provider";
import { Toaster } from "@/components/ui/sonner";

export function Providers({ children }: { children: ReactNode }) {
  const router = useRouter();
  const queryClient = getQueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider
        authClient={authClient}
        redirectTo="/settings/account"
        socialProviders={["google", "github"]}
        navigate={({ to, replace }) =>
          replace ? router.replace(to) : router.push(to)
        }
        Link={Link}
      >
        {children}
        <Toaster />
      </AuthProvider>
    </QueryClientProvider>
  );
}

Wire Providers in app/layout.tsx the same way you wrap theme providers. Pass framework navigate / Link so auth redirects use the App Router, not full page reloads.

app/auth/[path]/page.tsx
import { viewPaths } from "@better-auth-ui/core";
import { notFound } from "next/navigation";

import { Auth } from "@/components/auth/auth";

export default async function AuthPage({
  params,
}: {
  params: Promise<{ path: string }>;
}) {
  const { path } = await params;

  if (!Object.values(viewPaths.auth).includes(path)) {
    notFound();
  }

  return (
    <div className="my-auto flex justify-center p-4 md:p-6">
      <Auth path={path} />
    </div>
  );
}

Default auth paths include sign-in, sign-up, sign-out, forgot-password, and reset-password.

app/settings/[path]/page.tsx
import { viewPaths } from "@better-auth-ui/core";
import { ensureSession } from "@better-auth-ui/react/server";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { headers } from "next/headers";
import { notFound, redirect } from "next/navigation";

import { Settings } from "@/components/auth/settings/settings";
import { auth } from "@/lib/auth";
import { getQueryClient } from "@/lib/query-client";

export default async function SettingsPage({
  params,
}: {
  params: Promise<{ path: string }>;
}) {
  const { path } = await params;

  if (!Object.values(viewPaths.settings).includes(path)) {
    notFound();
  }

  const queryClient = getQueryClient();
  const session = await ensureSession(queryClient, auth, {
    headers: await headers(),
  });

  if (!session) {
    redirect(
      `/auth/sign-in?redirectTo=${encodeURIComponent(`/settings/${path}`)}`,
    );
  }

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <div className="mx-auto w-full max-w-3xl p-4 md:p-6">
        <Settings path={path} />
      </div>
    </HydrationBoundary>
  );
}

ensureSession hydrates the React Query cache during SSR so client hooks skip a loading flash. Settings paths typically include account and security.

Protecting routes with Better Auth UI

You need two layers for production SaaS dashboards: a server check so HTML never streams to anonymous users, and a client hook so sessions that die mid-visit still redirect. Official docs recommend SSR guard + useAuthenticate together.

Check the session in an async server component before streaming HTML. Redirect unauthenticated users immediately - no flash of dashboard chrome.

app/dashboard/page.tsx
import { ensureSession } from "@better-auth-ui/react/server";
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

import { auth } from "@/lib/auth";
import { getQueryClient } from "@/lib/query-client";

export default async function Dashboard() {
  const queryClient = getQueryClient();
  const session = await ensureSession(queryClient, auth, {
    headers: await headers(),
  });

  if (!session) {
    redirect("/auth/sign-in?redirectTo=/dashboard");
  }

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <h1>Hello, {session.user.email}</h1>
    </HydrationBoundary>
  );
}

Header UserButton tip

If a header or sidebar reads the session outside a protected page, prefetch the session in that subtree's server parent and wrap it in its own HydrationBoundary. Otherwise those components trigger a fresh client fetch on mount.

Better Auth UI components worth knowing

From the project llms.txt / docs index, the shadcn surface covers auth views, account chrome, settings, and email templates. Enable only what your Better Auth server already supports - a passkey button with no passkey plugin is a support ticket waiting to happen.

Auth surfaces

  • Auth - path-driven shell for all auth views
  • SignIn / SignUp - credentials, magic link, social
  • ForgotPassword / ResetPassword - recovery flow
  • VerifyEmail - confirmation + resend cooldown
  • SignOut - mounts and signs the user out

Account chrome

  • UserButton - avatar menu + sign out
  • UserAvatar / UserView - display primitives for headers and sidebars

Settings

  • Account - profile, change email
  • Security - change password, linked accounts, active sessions

Email templates

React Email-style templates for verification, magic link, reset password, OTP, new device, and organization invites - so UI and transactional mail feel like one product.

Plugins that expand the Better Auth UI surface

Better Auth UI plugins mirror Better Auth capabilities. Wire plugins on the server (Better Auth) and in AuthProvider (UI). Mismatched plugin lists are a top source of "button does nothing" bugs.

PluginWhat the UI adds
Magic LinkPasswordless email sign-in
PasskeyWebAuthn register / sign-in / device management
OrganizationMulti-tenant members, invites, roles
UsernameUsername sign-in + availability checks
API KeyCreate, copy, revoke keys in settings
CaptchaBot protection on auth forms
Delete UserAccount deletion with confirmation
Multi SessionSwitch between concurrent accounts
ThemeLight / dark / system in settings

Customization: own the code, keep the behavior

Because the shadcn path copies components into your repo, customization is normal product work. Prefer provider props and plugins over forking mutation logic so upstream registry updates stay mergeable.

Tokens - change CSS variables / Tailwind theme; auth cards follow the rest of the app.

Copy - localization props / strings for product language (not generic "Sign in").

Fields - additional fields on sign-up and profile when your user model needs them.

Layout - wrap Auth in your marketing shell, split-pane brand panel, or modal; keep form logic intact.

Providers - set socialProviders and Better Auth social configs to the same list.

Better Auth UI vs DIY shadcn forms vs a SaaS starter

This is the decision most "better auth ui" searchers are actually making. Pick Better Auth UI when auth screens are the bottleneck. Pick a starter when auth is one of ten blockers. Pick DIY when auth is the product.

ApproachTime to usable auth UIYou also getTradeoff
DIY forms calling authClientDays-weeksFull visual controlYou maintain every edge case
Better Auth UIHoursPolished auth + settings componentsStill DIY for billing, orgs, admin, mobile
TurboStarterMinutes for the whole foundationBetter Auth + billing + orgs + admin + multi-platformBroader kit than "just auth components"

See TurboStarter's auth overview and user flow for password, magic link, OTP, passkeys, OAuth, and 2FA views that ship with the kit.

Production checklist for Better Auth UI

Use this before you call auth "done":

Better Auth server + client share the same base URL and trusted origins

Email provider sends verification / reset / magic-link messages in staging

OAuth redirect URIs match production and preview domains
AuthProvider plugins match server plugins

Dashboard uses ensureSession (or equivalent server check) - not client-only gates

useAuthenticate covers long-lived client shells (headers with UserButton)

Password reset and email-change flows tested end-to-end
Rate limiting / captcha considered on public auth endpoints

Session cookies work across your real deploy host (HTTPS, correct domain)

Accessibility: labels, errors, and focus order checked on sign-in / sign-up

For a broader Next.js hardening pass after auth UI lands, read the Next.js security checklist.

Common Better Auth UI mistakes

  1. Installing UI before Better Auth works - prove signIn.email against your API first
  2. Client-only route protection - scrapers and prefetch still hit RSC payloads; guard on the server
  3. Shared QueryClient on the server - session cache leaks across users under load
  4. Legacy package docs on a new install - AuthUIProvider tutorials may not match current AuthProvider + registry files
  5. Enabling social buttons without provider credentials - empty OAuth config fails late, in the redirect
  6. Forgetting redirectTo - users bounce to / after login instead of the page they wanted
  7. Treating auth UI as the whole SaaS - you still need billing webhooks, org invites, and admin tools

Frequently asked questions

Conclusion

Better Auth UI is the missing presentation layer for Better Auth: shadcn-owned (or HeroUI/Solid) components for the auth and account flows every SaaS ships. Install the registry, wrap AuthProvider, add dynamic auth and settings routes, and protect dashboards with ensureSession plus useAuthenticate.

If your goal is only clean auth screens on an existing Next.js app, start with the official Better Auth UI docs. If your goal is a production SaaS - auth UI plus billing, organizations, admin, and a monorepo you can grow into mobile - skip wiring ten libraries and start from TurboStarter.

Sounds good?Now let's make it real. In minutes.
Try TurboStarter
world map
Community

Connect with like-minded people

Join our community to get feedback, support, and grow together with 600+ builders on board, let's ship it!

Join us

Ship your startup everywhere. In minutes.

Skip the complex setups and start building features on day one.

Get TurboStarter