krishna@
8 min read#tooling#web

Why I built @teispace/next-maker

Most Next.js teams keep starting from broken templates. So I built a CLI that ships an opinionated stack with TypeScript, Tailwind v4, Biome, Pino, Zod, Redux Toolkit, and a manifest-driven doctor — in one command.

share
Terminal close-up

the opening day problem

Every Next.js project I've started in the last three years begins the same way. create-next-app. Then a long opening day spent wiring everything a real production app needs but no starter ships: env validation, structured logging, a state library, i18n, a real test runner, a real linter, husky hooks that don't fight the team, a CI pipeline that actually mirrors what the husky hooks do, and theme tokens that work in dark mode without flashing.

I'd done this opening day three times in one quarter when I started writing a CLI to do it for me. Each repetition produced a slightly different result. Each was slightly worse than the last because I'd forgotten one of the constraints. None of them stayed in sync once a real codebase grew on top.

The CLI is what came out of that. It's called @teispace/next-maker, it's on npm, and it solves a problem most starters won't admit they have.

what init produces

npx @teispace/next-maker init my-app

Three minutes later you have a project with these decisions made:

  • Next.js 16 App Router, strict TypeScript with no any, ESM-first.
  • Tailwind v4 — note the v4. There's no tailwind.config.js; theme tokens live in CSS via the @theme block. Dark and light variants of every token are pre-mapped.
  • Biome for lint + format + import sort. One tool, sub-second runs. No ESLint plus Prettier two-step. The choice to drop ESLint is opinionated and saves real time.
  • Vitest + React Testing Library + jsdom, with a renderWithProviders helper for components that need Redux or i18n context.
  • Pino logger at @/lib/logger with auto-redaction of sensitive keys (token, password, authorization, cookie). All app code imports from here; nothing uses console.*.
  • Zod-validated env. @/lib/env parses process.env at module load and throws loudly on misconfig. This is better than discovering a missing var in production at 3am.
  • Redux Toolkit + redux-persist with typed hooks (useAppDispatch, useAppSelector).
  • next-intl for i18n, with locale-prefix never so URLs stay clean. Optional — next-maker remove i18n strips it cleanly.
  • husky with a pre-commit (env:sync + lint-staged + type-check) and a pre-push (ci:check + type-check + check:deprecated + tests). The build is reserved for CI.
  • commitlint with conventional commits, gated by a commit-msg hook.

Every choice has a reason. The README explains each one with the alternative considered.

the manifest is the killer feature

The interesting part isn't init — most starters can do init. The interesting part is keeping installs healthy over time.

Every file the CLI writes is recorded in a manifest: the path, a stable hash, and the feature it belongs to. This makes two non-trivial commands work.

npx @teispace/next-maker doctor

Reads your local install, walks the manifest, and reports drift. "Your tsconfig.json is two minor versions behind the template — here's the diff." "Your biome.json has a deprecated rule." "You're missing src/lib/env.ts — the env validator was added in v1.12 of the template."

This is a linter for your scaffolding, separate from the linter for your code.

npx @teispace/next-maker remove i18n

Walks the manifest in reverse for the i18n feature. Deletes src/i18n/, removes next-intl and its config, strips locale-aware routing, drops the [locale] segment of the App Router. The result: every file the CLI added for that feature, gone, with the surrounding code stitched back to "as if i18n was never there."

This matters because most starters become useless after week one. You can't safely re-clone over an existing app, and you can't cleanly opt out of pieces you didn't end up needing. The manifest is what makes both possible.

why a CLI and not a template repo

I tried template-repo first. The problem is that a template repo is a snapshot. The day you fork it, you've forked a moment in time. Every improvement after that has to be back-ported by hand into every fork, and most don't get back-ported at all. The world ends up full of stale Next.js starters with subtly broken Biome configs and three-month-old dependencies.

A CLI doesn't have that problem because it publishes. Every npx @teispace/next-maker init resolves to the latest published version. The template behind it (teispace/nextjs-starter) is what this very portfolio is built on, and every commit there gets pulled into the next CLI publish.

The CLI and the reference app evolve together. doctor lets users keep up.

the publish pipeline

The CLI's CI does three things:

  1. Runs the same quality gates the templates teach: Biome, type-check, deprecation check, tests.
  2. Bundles the latest template snapshot into the CLI's templates/ directory at publish time. Users always get today's template, not whatever was committed when the CLI itself was last touched.
  3. Auto-bumps the version with conventional-commits + changesets, publishes to npm.

Result: shipping a fix is one merged PR. The template gets the change → CLI republishes within minutes → doctor on existing installs lights up the next day.

what's missing (so far)

Two honest gaps:

The manifest format is too implicit. I infer feature ownership from file path patterns ("anything under src/i18n/ is the i18n feature"). It works today because the structure is mine, but if external contributors add features it'll bite. The next major version moves to an explicit features.ts declaration where each feature lists its files.

doctor reports drift but doesn't fix it. Currently it's read-only. There's a gnarly product question hiding in "auto-apply": users have local edits, those edits matter, and a naive doctor --fix would clobber them. The right answer is probably a three-way merge UI, which is real work.

the meta-point

A CLI is the right shape for this kind of work because it lets the canonical version travel. A template repo doesn't. Once the CLI exists, scaffolding becomes a continuous concern — something you check on the same way you check whether your dependencies have updates — instead of a one-shot decision you regret six months in.

If your team starts a Next.js project this quarter, run npx @teispace/next-maker init and skip the opening day. If you've cloned an old starter and feel the drift, run doctor. The CLI publishes weekly; the template behind it runs the site you're reading.

npx @teispace/next-maker init my-app

by Krishna Adhikari · Apr 22, 2026
share
// related.transmissions

Keep reading.