← Back to Blog
Product Dev6 min read·March 2026

How We Architect Next.js SaaS Products That Scale to 100k Users

Architecture decisions made in the first sprint define whether you will be refactoring at 10k users or comfortably handling 100k. Here is how we think about every Next.js SaaS we ship.

Why Architecture Matters Before You Need It

Most early-stage SaaS teams make the same mistake: they design for the users they have, not the users they are trying to get. A product that handles 500 users cleanly often needs a near-complete rebuild to handle 50,000 — not because the code is bad, but because the architecture was never designed to scale.

The good news is that building for scale does not mean building complexity upfront. It means making a handful of key decisions correctly in the first sprint — decisions that keep the path to scale open without adding unnecessary overhead to getting to launch.

Here is the architecture pattern we use for every Next.js SaaS product we ship.

SaaS Architecture Stack

FrontendAPI LayerServicesDataNext.js AppApp Router + RSCCDN / EdgeVercel / CloudflareAPI RoutesREST + tRPCAuthNextAuth v5Rate LimitingUpstash RedisUser ServiceProfiles + OrgsBillingStripe WebhooksQueue WorkersBullMQ / InngestPlanetScaleMySQL / PrismaRedis CacheUpstash / ElastiCacheBlob StorageS3 / Cloudflare R2

Layer 1: The Next.js Edge

The App Router is not just a file-based routing system. Used correctly, it is a full-stack architecture layer. React Server Components render on the server (or at the edge), reducing JavaScript bundle size and eliminating client-side data fetching for most pages.

We keep the client boundary as far down the component tree as possible — only components that need interactivity (forms, dropdowns, real-time state) are marked as Client Components. Everything else stays server-rendered. This alone cuts First Contentful Paint by 40–60% in most apps we migrate.

The Multi-Tenant Data Model

Getting multi-tenancy right from the start is the single most important architectural decision in a B2B SaaS. There are three approaches: shared schema (all customers in one table, filtered by org_id), schema-per-tenant (separate Postgres schemas), and database-per-tenant (maximum isolation, highest cost).

We default to shared schema with row-level security for startups. The data model is simpler, the operational overhead is lower, and row-level security in PlanetScale or Postgres provides the isolation that regulated customers need. Moving to schema-per-tenant later is tractable; retrofitting multi-tenancy onto a single-tenant schema is not.

Always add org_id to every row from day one

Even if you launch with single-tenant mode, the column needs to be there. Retrofitting it costs weeks.

Never trust the client to send org_id

Always derive it server-side from the session. Client-side org selection is a permissions bug waiting to happen.

Use database-level RLS for sensitive tables

Prisma middleware handles most filtering, but for financial data or PII, add a second enforcement layer at the database.

Performance Numbers That Actually Matter

<800ms

target TTFB on all routes

95+

Lighthouse performance score

99.9%

uptime SLA we design for

100k+

concurrent users before rearchitect

Building a SaaS product?

We design and build Next.js SaaS products from first principles — architecture review, full-stack development, or both. Tell us what you are working on.

Start a Conversation →

Related Articles

AI Automation

How We Cut a SaaS Company's Support Volume by 60% Using AI Agents

Cloud Infrastructure

Cloud Cost Optimisation: How We Reduced AWS Bills by 40% for a Fintech Startup