A starter template for authentication built with Next.js 16, Better Auth, shadcn/ui, and Supabase PostgreSQL.
- Email & Password Authentication — Sign up, sign in with email and password
- OAuth Social Login — GitHub, Google, Discord, Slack out of the box
- Dynamic Provider Discovery — Easily add new OAuth providers with minimal code changes
- Protected Routes — Cookie check via
proxy.ts+ server-side session validation on protected pages - User Dashboard — Profile info and session details
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Authentication | Better Auth |
| UI | shadcn/ui + Tailwind CSS 4 |
| Database | Supabase PostgreSQL |
| ORM | Drizzle ORM |
proxy.ts # Route interceptor (cookie-based check)
drizzle.config.ts # Drizzle Kit config
app/
api/auth/[...all]/route.ts # Better Auth API handler
(public)/
login/ # Sign in page
register/ # Sign up page
(protected)/
dashboard/ # User dashboard (server-side auth)
page.tsx # Landing page
not-found.tsx # 404 page
layout.tsx # Root layout
globals.css # Global styles (Tailwind + shadcn theme)
components/
auth/
social-login-button.tsx # Dynamic OAuth buttons
provider-icons.tsx # Provider SVG icons
logout-button.tsx # Sign out button
ui/ # shadcn/ui components
lib/
auth.ts # Better Auth server config
auth-client.ts # Better Auth client
providers.ts # OAuth provider dynamic discovery
db/
index.ts # Drizzle database instance
schema.ts # Database schema
utils.ts # Utility functions (cn)
- Node.js 18+
- A Supabase project (for PostgreSQL database)
npm installCopy the example file and fill in your values:
cp .env.example .env# Database (PostgreSQL connection string)
DATABASE_URL=postgresql://user:password@host:port/database
# Better Auth
BETTER_AUTH_SECRET=your-secret-key-at-least-32-characters-long
BETTER_AUTH_URL=http://localhost:3000 # Change to your domain in production
# OAuth Providers (configure to enable, leave empty to disable)
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
SLACK_CLIENT_ID=
SLACK_CLIENT_SECRET=npm run db:pushnpm run devOpen http://localhost:3000 to see the app.
This template supports dynamic provider discovery. To add a new provider (e.g., Twitter):
-
Add the provider's
clientIdandclientSecretenv vars to.env:TWITTER_CLIENT_ID=your-client-id TWITTER_CLIENT_SECRET=your-client-secret
-
Register the provider in
lib/providers.tsby adding an entry to theallProvidersarray. -
Add the provider icon in
components/auth/provider-icons.tsx.
The login and register pages will automatically display the new provider button.
| Command | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Build for production |
npm run start |
Start production server |
npm run lint |
Run ESLint |
npm run db:generate |
Generate Drizzle migrations |
npm run db:migrate |
Run Drizzle migrations |
npm run db:push |
Push schema directly to database |
npm run db:studio |
Open Drizzle Studio (database GUI) |
| Path | Auth Required | Description |
|---|---|---|
/ |
No | Landing page |
/login |
No | Sign in page (redirects to /dashboard if already logged in) |
/register |
No | Sign up page (redirects to /dashboard if already logged in) |
/dashboard |
Yes | User dashboard (redirects to /login if not logged in) |
MIT