The Next.js SaaS Stack I Actually Use in 2026
After shipping eight production SaaS products, here's the exact stack, file layout, and architectural decisions I make on every greenfield Next.js project — and the ones I avoid.
Most "best Next.js stack 2026" articles read like a shopping list. After shipping eight production SaaS products, here's the stack and the file layout I actually reach for — and a few things I avoid.
The stack
Frontend & framework
- Next.js 14+ (App Router). Server components by default. Route groups for marketing vs. authenticated app. ISR where it makes sense; dynamic rendering where it doesn't.
- TypeScript strict. No exceptions.
strict: true,noUncheckedIndexedAccess: true,exactOptionalPropertyTypes: true. - Tailwind CSS. I tried alternatives. None of them came close for the velocity of a one-person team.
- Framer Motion + Lucide. For motion and icons. Small, predictable, zero-config.
- Zod. For runtime validation at every trust boundary — API routes, form submissions, webhooks, AI responses.
Backend & data
- PostgreSQL. Always. It's the right default for almost every SaaS product from zero to a million users.
- Prisma or Drizzle. I reach for Drizzle for greenfield projects in 2026 — better TypeScript inference, faster cold starts, no schema migration surprises. Prisma is fine; Drizzle is better.
- Redis when I need queues, rate limits, sessions, or pub/sub. Upstash for serverless.
- NextAuth (Auth.js) v5 for auth, with email + OAuth providers. Database sessions for B2B; JWT for B2C.
AI
- OpenAI as the default LLM. Anthropic Claude for long-context summarization and structured outputs. Ollama for local-first / regulated workloads.
- LangChain only when the orchestration is genuinely complex. For most
products, the OpenAI / Anthropic SDK + a small
chat()wrapper is enough. - pgvector for embeddings. Add it to your Postgres from day one; the upgrade cost later is real.
Infra
- AWS (Amplify for Next.js, RDS for Postgres, S3 for assets, CloudFront for CDN). Amplify's Next.js support is genuinely good in 2026.
- GitHub Actions for CI.
- Sentry for errors. PostHog for product analytics.
- Resend or SMTP for transactional email.
File layout that scales
app/
(marketing)/ ← public site (home, pricing, blog)
(app)/ ← authenticated app (dashboard, settings)
api/ ← route handlers
components/
ui/ ← primitives (button, input, modal)
features/ ← feature-level components (chat, billing, etc.)
marketing/ ← landing-page sections
lib/
auth/ ← auth helpers
db/ ← DB client + queries
ai/ ← LLM wrappers, prompts, evals
email/ ← transactional email templates
config.ts ← env-validated config
content/
blogs/ ← markdown blog posts
types/
public/The parentheses around (marketing) and (app) are route groups. They
share a layout, organize URLs cleanly, and let you set different cache /
middleware rules per group without rewriting everything.
Decisions I make on day one
Authentication from line one
Don't bolt auth on later. Add NextAuth on the first commit. Even if your MVP is "logged in only," you'll thank yourself.
Env validation at boot
Use Zod to validate process.env at boot. Fail fast if a config var is
missing. Saves hours of debugging in production.
// lib/config.ts
import { z } from "zod";
const schema = z.object({
DATABASE_URL: z.string().url(),
OPENAI_API_KEY: z.string().min(1),
AUTH_SECRET: z.string().min(32),
STRIPE_SECRET_KEY: z.string().min(1).optional(),
});
export const config = schema.parse(process.env);Logging that survives
Don't console.log in production. Use a thin wrapper that pipes to
Sentry / Datadog / your log aggregator. Add request IDs. Tag with user ID
when authenticated. You'll thank yourself at 2am.
Error boundaries at the route level
Every meaningful route gets an error.tsx. Every feature gets a
<ErrorBoundary> at the top. Errors should never crash the whole app.
Rate limits at the edge
Add rate limits to every public API route on day one. Upstash + middleware makes this ~20 lines of code.
Things I avoid
tRPC
Lovely DX for solo work. Painful for multi-team codebases. For solo SaaS,
just use plain fetch and typed route handlers — the boilerplate is fine
and the surface area is smaller.
Microservices
Don't split until you have a team of ten engineers and a clear reason. A
single Next.js app with a clean lib/ directory serves most products to
their first million users.
ORM-heavy abstractions
Drizzle and Prisma both have a sweet spot. Use the query builder for complex reads. Use the model API for simple CRUD. Don't wrap everything in repositories "for cleanliness" — it just adds indirection.
Heavy CSS-in-JS
I tried Emotion, styled-components, and Stitches. Every one of them produced measurable bundle bloat and runtime overhead. Tailwind + a few CSS variables wins.
Custom UI kits
Use Radix UI or shadcn/ui. Don't build your own dialog or dropdown from scratch — accessibility is harder than it looks.
The boring decisions that pay off
Use a monorepo only when you need one
For a single SaaS, a flat Next.js repo is correct. Add a monorepo when you're shipping two products, a marketing site, and a mobile app. Not before.
Pin your Node version
engines.node in package.json. .nvmrc in the repo. CI uses the same
version as dev. Reproducible builds matter.
Test the things that break
I don't aim for 100% coverage. I write tests for the things that have already broken — billing, auth, AI prompts, data migrations, anything that touches money or PII.
Document the things that matter
A README.md that gets new engineers to "Hello World" in 15 minutes. A
/docs page in the app for the non-obvious product flows. A CHANGELOG.md
that you actually keep up to date.
How long it takes to ship
A clean Next.js SaaS MVP — auth, billing, dashboard, one core feature, basic admin — takes me about 4 weeks of focused work. The same product on a "popular JavaScript framework" would take 8. The reason isn't the framework; it's the discipline of making good decisions early and not rewriting them.
What I'd do differently
If I were starting today:
- I'd skip the Redux/Zustand debate entirely and reach for TanStack Query + server components. Almost all "state" in a SaaS is server state.
- I'd add observability from day one — Sentry + PostHog + a request log. Skipping this has cost me a week per project.
- I'd write the boring docs first — architecture decisions, env var reference, deploy runbook. Documentation debt compounds faster than code debt.
If you're starting a Next.js SaaS and want to skip the architecture mistakes, I take on a small number of greenfield engagements each quarter.