nextjsboilerplate Docs
Guides

Initial setup

Clone, configure, migrate, seed, run. The reproducible path from a bare checkout to a fully wired SaaS skeleton with audit chain, billing, and demo data.

Initial setup

This guide walks through the full initial setup. The Quickstart shows the four-command happy path. This page is for the moments when the happy path doesn't fit — when you want to know what each step actually does, in what order, and what to do when something complains.

Prerequisites

  • Node.js — version >= 22.20.0 (matches package.json engines).
  • npm>= 10. The boilerplate uses package-lock.json. Yarn and pnpm are not officially supported.
  • Git — for cloning. No submodules.
  • No Docker required — the local database is PGlite (file-backed), no Postgres install needed.

1. Clone the repository

git clone https://github.com/ixartz/Next-js-Boilerplate.git my-saas
cd my-saas

Rename the upstream remote so you don't accidentally push back to it :

git remote rename origin upstream
git remote add origin git@github.com:your-org/your-fork.git

2. Install dependencies

npm install --legacy-peer-deps

The --legacy-peer-deps flag is mandatory. Storybook 10, Chromatic, Playwright, and React 19 have peer-dep ranges that npm 10 refuses to auto-resolve under strict mode. Every CI step in .github/workflows/ uses the same flag.

3. Create a local environment file

The boilerplate works with no .env.local at all for local dev — every external provider is optional() in src/libs/Env.ts and degrades to a no-op. Still, it is useful to copy the template so you have a starting point :

cp .env.example .env.local

The template at .env.example is heavily commented and grouped by surface (APP, AUTH, DATABASE, STRIPE, EMAIL, STORAGE, OBSERVABILITY, LLM, SECURITY). Each var is tagged :

  • # REQUIRED — boot fails or core surface broken without it.
  • # RECOMMENDED — boot succeeds, surface degrades to a no-op.
  • # OPTIONAL — purely additive integration.

For first-run local dev, only one var matters :

# .env.local
AUTH_SECRET=dev_secret_at_least_32_characters_long_xxxxx

Generate a real one with :

openssl rand -base64 32

Everything else can stay empty for the first dev session — Stripe, Resend, Sentry, PostHog, Arcjet, S3, Anthropic, OpenAI all have safe fallbacks.

4. Run database migrations + boot the local database

npm run db:setup

This single command does three things :

  1. Boots a PGlite Postgres on port 5433, file-backed at local.db/.
  2. Runs every Drizzle migration in migrations/ in order.
  3. Stays running so the app can connect.

npm run db:setup is idempotent — re-running it on top of an existing local.db/ only applies new migrations. If the database ever gets into a weird state, just delete the directory and re-run :

rm -rf local.db
npm run db:setup

If you run a remote Postgres instead (managed RDS, Supabase, Neon), set DATABASE_URL in .env.local and run the migrations directly :

npm run db:migrate

db:migrate reads DATABASE_URL from the env and applies the same migration set without booting PGlite.

5. Seed demo data

npm run db:seed:demo

The seed script at scripts/seed-demo.ts populates :

  • A dev user (dev@example.com / any password — sign-in is permissive in dev).
  • A demo organization with the dev user as owner.
  • Sample subscription rows (active pro tier).
  • 50 audit-log rows with mixed kinds (user.signed_in, credits.consumed, invite.sent, etc.).
  • A handful of audit_chain rows so the chain verifier has something to walk.
  • 25 credits-ledger entries.
  • Three system-wide evidence checklists (chargeback, leakage, compliance).
  • Two sample webhook endpoints + 10 deliveries (mixed success / failed).
  • Five seeded LLM prompts.

The seed is idempotent on dev@example.com — running it twice does not double-seed. Drop local.db/ if you want a clean slate.

6. Run the dev server

npm run dev

The dev server boots on http://localhost:3000 with :

  • Next.js 16 App Router + React Compiler.
  • Turbopack for dev (HMR).
  • Tailwind v4 CSS-first compilation.
  • Sentry / PostHog / OpenTelemetry disabled unless their env vars are set.

Open the browser :

  • / — marketing landing page.
  • /sign-in — credentials + OAuth (only the providers with env vars wired).
  • /sign-up?invite=… — invite acceptance flow (auto-accept on sign-up).
  • /dashboard — the operational surface, gated behind sign-in.
  • /dashboard/audit-chain — the SHA-256 hash chain viewer (admin only).
  • /dashboard/webhooks — outbound and inbound webhook management.

Sign in as dev@example.com (any password works in dev when no AUTH_SECRET-bound session exists).

7. Verify the install

Run the standard local checks before you start coding :

npm run lint
npm run check:types
  • npm run lint — ESLint over **/*.{ts,tsx,mjs,mts} with the @antfu config and the local Next.js plugin.
  • npm run check:typestsc --noEmit --pretty. Strict mode, no skip.

Both must exit 0 before you commit. The lefthook pre-commit at lefthook.yml runs them automatically.

For the full QA gate :

npm run test         # vitest run — unit + integration
npm run test:e2e     # playwright — see playwright.config.ts
npm run check:i18n   # translation completeness en + fr
npm run build:next   # next build — production bundle check

Common issues

EADDRINUSE: address already in use :::5433

A previous npm run db:setup is still running. Kill it :

# unix
lsof -i :5433 | awk 'NR>1 {print $2}' | xargs kill -9

# windows powershell
Get-NetTCPConnection -LocalPort 5433 | Select-Object -Expand OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force }

ENOENT: no such file or directory, open 'local.db/...'

You ran npm run dev before npm run db:setup. Run them in order, or in two separate terminals. The dev server expects PGlite to be reachable on 5433.

LlmProviderKeyMissing: ANTHROPIC_API_KEY

You hit a code path that calls Llm.complete() without setting the provider key. Either set the key in .env.local, or trace the call site and gate it behind a feature flag.

Email dispatched to stdout (no provider key)

This is expected when RESEND_API_KEY is unset. The email body is printed to your terminal — copy the rendered HTML block into a browser to preview. Set RESEND_API_KEY=re_… to dispatch through Resend instead.

What's next

  • Authentication — wire OAuth providers and explain the Better Auth migration roadmap.
  • Billing & subscriptions — Stripe setup, the three tiers, checkout, customer portal.
  • Email layer — Resend, templates, per-category preferences, HMAC unsubscribe links.

On this page