Choosing a stack is mostly an exercise in choosing which problems you would like to have. Every tool trades something away, and the honest question is not which is best but which failure modes you can live with two years from now, when the original developer has moved on and the business wants to change something on a Friday afternoon.

These are the choices we have converged on, and why.

TypeScript, everywhere, without exceptions

The single highest-return decision we make on any project is that all of it is typed — the frontend, the API handlers, and the shared contracts between them. The cost is a modest amount of extra ceremony while writing. The return is that an entire category of bug simply stops occurring, and that any developer can open the codebase months later and have the compiler explain how it fits together.

On a client project this matters more than it does on a personal one, because the person maintaining it is very often not the person who wrote it.

React for interfaces, Vite for everything around it

React is not the most elegant framework available, and we do not pretend otherwise. It is the one with the deepest hiring pool, the most mature ecosystem, and the longest track record of not abandoning its users. For a client who needs to be able to hire someone else if we get hit by a bus, that is the deciding argument.

Vite handles the build. Cold starts in under a second, hot module replacement that actually preserves state, and a production build that is aggressively code-split by route without us hand-tuning anything. It removed an entire class of configuration work from our week.

Tailwind, and the design-system discipline it forces

Tailwind's real value is not that it is fast to write, though it is. It is that it makes inconsistency visible. When spacing, colour and type are constrained to a token scale, a design that drifts becomes obvious in the diff rather than three months after launch.

We pair it with semantic CSS variables for theming, so light and dark modes are a token swap rather than two parallel sets of styles that inevitably fall out of sync.

The rest of the toolkit

  • Framer Motion for animation — a shared preset module keeps every transition on the same easing curves instead of thirty hand-rolled durations.
  • Radix primitives for anything with real accessibility requirements: dialogs, menus, tooltips. Keyboard and screen-reader behaviour is not something to reimplement.
  • Zod for validating anything crossing a boundary, with the types inferred from the schema so validation and typing cannot disagree.
  • React Query for server state, because caching, retries and invalidation are genuinely hard and worth delegating.
  • A build-time prerender step for SEO, so a client-rendered app still serves correct per-route metadata to crawlers.

The parts that are not the stack

It is worth saying plainly that the technology choices above account for less of a project's success than the practices around them. A well-chosen stack badly operated produces worse outcomes than a mediocre stack run with discipline.

So: every project is in version control from the first commit. Every deployment is reproducible from a clean checkout. Environment configuration lives in the platform, never in the repository. Errors are reported somewhere a human will actually see them. These are unremarkable expectations in a product engineering team and startlingly rare in agency-delivered work.

What we deliberately avoid

We do not add a state management library until the application demonstrably needs one; most do not. We do not reach for a CMS on a site with a dozen pages that change twice a year. And we do not adopt a framework in its first year of life on a project someone else has to maintain.

We are similarly cautious about microservices, custom design systems and anything else whose benefits arrive at a scale our clients do not operate at. Architecture that would be prudent for a company of two hundred engineers is, for a company of twelve people, simply a larger surface area to maintain with the same amount of time.

The honest test we apply is whether we could explain the choice to the client in one sentence and have them agree it was worth their money. Most exotic decisions do not survive that test.

The best stack is the one your client can still afford to change their mind about in year three.

None of this is exotic. That is rather the point — a stack should be interesting for what you build with it, not for itself.