Production‑ready authentication & account management starter for Next.js 15 (App Router) using Better Auth, Drizzle ORM (PostgreSQL), Tailwind CSS v4, shadcn/ui, and prebuilt flows from @daveyplate/better-auth-ui.
- Clone the repo
git clone https://github.com/mtcnbzks/better-auth-starter.git cd better-auth-starter
- Install dependencies
npm install
- Create
.env.local
cp .env.example .env.local # then fill: # DATABASE_URL=postgres://USER:PASSWORD@HOST:5432/DB # GOOGLE_CLIENT_ID=... # GOOGLE_CLIENT_SECRET=...
- (If using Neon) Create a new Neon project and copy the pooled connection string into
DATABASE_URL
. - Push the schema to your database (creates auth tables)
npm run db:push
- (Optional) Open Drizzle Studio to inspect tables
npm run db:studio
- Start the dev server
npm run dev
- Visit
http://localhost:3000/auth/sign-in
and use Google sign‑in.
Next steps: add more OAuth providers, enable email/password, or extend the user
table.
Layer | Tech | Notes |
---|---|---|
Framework | Next.js 15 (App Router, RSC) | TurboPack dev server enabled |
Auth Core | better-auth | Google social login enabled; credentials-ready (commented) |
Auth UI | @daveyplate/better-auth-ui | Prebuilt sign-in / sign-up / reset / account views |
DB / ORM | Drizzle ORM + Postgres (Neon) | Typed schema + migrations (drizzle-kit) |
Styling | Tailwind CSS v4 + next-themes | Dark / light mode |
Component Primitives | shadcn/ui + Radix | Headless accessible components |
- Social login: Google (easy to add more providers)
- (Optional) email + password flow scaffolded (commented – enable when ready)
- Account management pages (profile, sessions, security) pre-rendered statically
- Protected routes via Next.js Middleware cookie check
- Fully typed Drizzle schema for auth tables (
user
,session
,account
,verification
) - Server auth instance (
auth
) - Light / dark theme toggle
- Ready for serverless Postgres (Neon) out of the box
src/
app/
auth/[path]/page.tsx # Auth flows (sign-in, sign-up, reset, etc.) generated from UI lib
account/[path]/page.tsx # Account management routes
layout.tsx # Root layout & providers
page.tsx # Example landing page
auth/
index.ts # better-auth server configuration
client.ts # better-auth React client helpers
db/
schema.ts # Drizzle schema definitions
index.ts # Drizzle init (Postgres driver)
middleware.ts # Protect selected routes
components/ # Shared UI (header, theme toggle, primitives)
lib/utils.ts # Utility functions
Auth & account routes are static‑param generated for pre-rendering. generateStaticParams()
enumerates allowed path segments from the UI library exports (authViewPaths
,
accountViewPaths
).
Create a .env.local
(never commit) with:
DATABASE_URL="postgres://USER:PASSWORD@HOST:PORT/DB"
GOOGLE_CLIENT_ID="your-google-oauth-client-id.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="your-google-oauth-client-secret"
Recommended (Neon): copy the pooled connection string for serverless usage.
If you later enable email/password or magic links you may need additional SMTP variables (not included yet).
Generate / push schema (Drizzle uses the TypeScript schema as source of truth):
npm run db:push # Push schema to database
npm run db:studio # Visual schema browser
Tables defined:
user
: core profile & metadatasession
: active sessions (token, expiry, ip, userAgent)account
: OAuth provider accounts + tokensverification
: short‑lived verifications (email, password reset, etc.)
Defined in src/auth/index.ts
:
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg", schema }),
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
plugins: [nextCookies()],
});
Uncomment the emailAndPassword
block to add credentials flow.
src/middleware.ts
checks for a session cookie and redirects unauthenticated users to /auth/sign-in?redirectTo=...
for paths matched by:
export const config = { matcher: ["/auth/settings"] };
Add more paths (e.g. /dashboard
, /billing
) as you build private areas.
Server components can also call auth.session()
(or similar helper) directly; client components can use hooks from better-auth/react
.
Powered by next-themes
and Tailwind CSS v4. The mode-toggle
component switches between light
and dark
.
Follow the steps in Getting Started. Then visit http://localhost:3000/auth/sign-in.
Add provider credentials (e.g. GitHub) then extend socialProviders
in src/auth/index.ts
. Add corresponding env vars and restart.
Issue | Fix |
---|---|
OAuth redirect mismatch | Ensure exact redirect URI in provider console matches deployed URL + /api/auth/callback/google |
Sessions not persisting | Check domain / path of cookie; verify nextCookies() plugin included last |
DB errors on startup | Confirm DATABASE_URL & run npm run db:push to create tables |
Provider button absent | Ensure provider configured & env vars present at build time |
MIT – use freely in commercial or personal projects.
Happy building! PRs & improvements welcome.