A Next.js boilerplate with authentication, database, storage, and i18n pre-configured. Scaffold new projects and build features on top of a maintained base.
- Next.js 16 with App Router
- NextAuth v5 (credentials, Google OAuth, Resend magic link)
- PostgreSQL with Drizzle ORM (users, accounts, sessions schema)
- MinIO (S3-compatible object storage)
- Tailwind CSS 4 with shadcn/ui components
- lechnerio-git-hooks (commitlint, lint-staged, versioning)
- Optional i18n via next-intl
npx create-next-plate my-app
cd my-app
# Fill in AUTH_SECRET in .env (generate with: openssl rand -base64 32)
pnpm devpnpm dev starts Docker containers (Postgres + MinIO), generates .env.example, pushes the schema, and runs the Next.js dev server. The .env file is scaffolded with local defaults — just add your AUTH_SECRET.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
AUTH_SECRET |
Yes | NextAuth secret (random 32+ char string) |
AUTH_URL |
Yes | App URL (e.g. http://localhost:3000) |
AUTH_GOOGLE_ID |
No | Google OAuth client ID |
AUTH_GOOGLE_SECRET |
No | Google OAuth client secret |
RESEND_API_KEY |
No | Resend API key for magic link emails |
MINIO_ENDPOINT |
Yes | MinIO endpoint (e.g. http://localhost:9000) |
MINIO_ACCESS_KEY |
Yes | MinIO access key |
MINIO_SECRET_KEY |
Yes | MinIO secret key |
NEXT_PUBLIC_MINIO_ENDPOINT |
Yes | Public MinIO URL for image serving |
NEXT_PUBLIC_SITE_URL |
No | Public site URL |
Files from the boilerplate are distinguishable by naming:
plate.prefix on lib/config files:plate.middleware.ts,plate.docker-compose.yml,lib/plate.db.ts,lib/plate.auth.ts@plate-managedcomment on App Router files that require exact filenames (layouts, pages, routes) — tracked inplate.json
Project-specific files have no prefix and live alongside plate files.
my-app/
plate.json # tracks plate-managed files
plate.middleware.ts # auth route protection
plate.docker-compose.yml # postgres + minio
plate.drizzle.config.ts # drizzle config
.env # your env (gitignored)
.env.example # auto-generated on build/dev
app/
layout.tsx # plate-managed root layout
page.tsx # your starter page
(auth)/signin/page.tsx # plate-managed auth pages
api/auth/[...nextauth]/ # plate-managed auth API
api/auth/register/ # plate-managed registration API
lib/
plate.db.ts # database connection
plate.auth.ts # auth configuration
plate.storage.ts # MinIO storage
plate.utils.ts # cn(), validators
db/schema.ts # base schema + your tables
components/
plate.data-table.tsx # reusable data table
plate.user-menu.tsx # user dropdown menu
plate.theme-toggle.tsx # dark/light mode toggle
ui/ # shadcn components
Add project-specific tables in lib/db/schema.ts alongside the base exports:
export { users, accounts, sessions, verificationTokens, siteSettings } from "next-plate/schema"
import { pgTable, text, timestamp } from "drizzle-orm/pg-core"
import { users } from "next-plate/schema"
export const posts = pgTable("post", {
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
authorId: text("authorId").notNull().references(() => users.id, { onDelete: "cascade" }),
content: text("content"),
createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(),
})Then run pnpm db:push to apply changes.
When next-plate releases updates:
pnpm update next-plate
npx next-plate updateThe update CLI compares file hashes and prompts for each modified file: accept the update, skip it, or eject the file from tracking.
To permanently remove a file from plate management:
npx next-plate eject lib/plate.auth.ts| Package | Purpose |
|---|---|
next-plate |
Runtime dependency with factories (createDb, createAuth, createStorage) and update CLI |
create-next-plate |
Scaffolder CLI for new projects |