Lessons from Shipping a Next.js SaaS and Building a Scaffolding CLI
Explains the foundational decisions—stack choices, auth, multi-tenancy, billing, and state management—plus building a CLI that streamlines Next.js project setup.
This article is published in English.
Shipping a real SaaS product with React and Next.js is less about picking a trendy library and more about making dozens of small, unglamorous decisions correctly before you ever touch a dashboard. The same discipline that makes a product architecture hold up under real customers also shows up in something as mundane as scaffolding a new Next.js project — which is why a homegrown CLI tool for generating boilerplate ends up teaching the same lesson as building the SaaS itself: the boring setup work is where projects actually succeed or fail.
Picking a Stack That Won't Slow You Down
After shipping more than one SaaS product, a stack tends to settle into a predictable shape:
- Next.js as the single framework for routing, server-side rendering, and API routes, so you're not juggling separate tools for each concern.
- TypeScript, non-negotiably, especially anywhere billing logic lives — those are not the kind of bugs you want surfacing at runtime.
- Tailwind CSS for styling, mainly because it sidesteps the constant fight against CSS specificity.
- Redux Toolkit or Zustand, reserved strictly for state that genuinely needs to be shared globally.
None of these choices are made for novelty's sake. Together they let a small team move quickly without quietly building up technical debt they'll never have time to repay.
Build the Unglamorous Foundations First
It's tempting for founders to jump straight into building dashboards. Resist that urge. Before anything user-facing, get these three things in place:
- Authentication — implemented with real sessions, not just tokens stuffed into localStorage.
- Multi-tenancy — even if you only have a single customer today, design for more than one from the start.
- Billing hooks — wiring up Stripe webhooks early avoids a painful rewrite once the product actually launches.
Here's a pattern worth adopting for making tenant-scoped API calls without repeating yourself:
// hooks/useTenantFetch.ts
import { useCallback } from "react";
import { useSession } from "next-auth/react";
export function useTenantFetch() {
const { data: session } = useSession();
const fetchScoped = useCallback(
async (endpoint: string, options: RequestInit = {}) => {
if (!session?.tenantId) throw new Error("No active tenant");
const res = await fetch(`/api/${session.tenantId}${endpoint}`, {
...options,
headers: {
...options.headers,
"Content-Type": "application/json",
},
});
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
return res.json();
},
[session]
);
return { fetchScoped };
}
It's a small utility, but it saves you from scattering tenant-ID checks across dozens of components later.
Knowing What Belongs in Global State
Not every piece of data deserves a place in Redux. One engineer, working at a Y Combinator-backed startup, put it well: global state is debt that accrues interest over time. Reserve Redux (or your global store of choice) strictly for things that are genuinely UI-wide — modals, feature flags, user preferences — and let React Query or SWR handle everything else that's really just server data in disguise.
A few takeaways summarize the SaaS-building side of things:
- Get authentication and billing working before investing in polished UI.
- TypeScript stops being optional the moment money is involved.
- Resist over-centralizing state; most features belong to server-state tools, not to a global store.
None of this comes from chasing the newest library. It comes from fundamentals: nail down auth, tenancy, and billing early, and everything that follows — growth, new features, scale — becomes far easier to build on top of.
When the Setup Itself Becomes the Bottleneck
That same instinct to eliminate repetitive, error-prone setup work is what eventually leads to automating the boilerplate step itself. Every frontend developer knows the routine: you scaffold a new project with create-next-app, and then the actual tedium starts. You create a components folder, split ui from features, set up hooks, wire up shared types, write utility helpers, configure path aliases, and organize the folder hierarchy — and an hour can pass before you've written a single line of real business logic.
After going through that ritual too many times, the natural next step was to automate it, resulting in a first published open-source npm package: nexarch-cli.
The Underlying Problem
Standard Next.js starter templates give you a blank canvas, not an architecture ready for production. Modern applications generally need:
- A clear separation between presentation logic and business logic
- Feature-based modular grouping
- Scalable type definitions and utility wrappers
- Consistent conventions that a whole team can follow
Recreating all of this by hand for every prototype or client project is slow, inconsistent, and prone to mistakes.
What nexarch-cli Actually Does
nexarch-cli is an interactive command-line tool built to scaffold a clean, modular Next.js architecture within seconds. Rather than manually building out nested folders and placeholder files, you answer a handful of prompts in your terminal, and the tool sets up the baseline structure for you.
Its main features include:
- Interactive scaffolding through simple terminal prompts tailored to what you need.
- Modular architecture with conventions pre-configured to scale as the application grows.
- No global installation required — it runs on demand through
npx. - Alignment with current Next.js and React conventions, keeping directory structures consistent with modern practices.
Running It
There's no need to install anything globally. You can run it directly:
npx nexarch-cli
Once you answer the prompts, your project structure is generated and ready for actual development.
Lessons From Building a First CLI
Building and publishing this as a first developer tool came with a steep learning curve. A few of the bigger lessons:
- Terminal UX is as important as the underlying code. Handling process exits cleanly, presenting readable prompts, and surfacing clear error messages all matter just as much as the logic that generates files.
- File system operations hide plenty of edge cases. Writing recursive directory creators and file templates that behave consistently across different operating systems demands careful, repeated testing.
- Packaging for npm has its own learning curve. Getting the
package.jsonbinaries (bin) configured correctly, handling file permissions withchmod, and keeping dependency overhead near zero all revealed how Node-based CLI tools work under the hood.
Where the Project Stands Now
The tool remains an early, open-source effort, and it benefits from outside input while it's still taking shape. The code lives at github.com/CaffeineLogs/nexarch-cli, and the published package can be found on the npm registry under the name nexarch-cli.
Anyone with a different favored project layout, or who runs into a scenario the scaffolder doesn't yet handle well, is welcome to raise it as an issue in the repository. Suggestions for what could come next — things like bundled styling setups or ready-made state-management presets — are equally welcome as discussion points or comments.
Whether you're deciding what belongs in Redux or deciding how a fresh Next.js project should be laid out, the lesson repeats itself: the parts of a project that feel most tedious — auth, tenancy, billing, folder structure — are exactly the parts worth getting right, and often automating, before anything else.