Infrastructure · We run it ourselves
A React framework that decides, per route, whether a page is rendered once at build time or computed on every request.
This site is Next.js 16.2.12 with React 19.2.4 and Tailwind v4, and the version is not decoration — /version names the exact build a reader is looking at. The interesting property is not that Next.js renders React on a server; it is that the rendering strategy is a per-route decision rather than a project-wide one, so a page of prose costs nothing to serve while a crawler census that recomputes hourly costs only what it actually computes. The price of that granularity is a framework that changes its own conventions faster than any other dependency here, and it is paid in reading time on every upgrade.
01
A single-page app makes the first meaningful answer wait for a JavaScript bundle to download, parse and execute before any text exists in the document. For a practice whose pages are read by automated clients as often as by people, that is the wrong trade in the wrong direction: /crawler-census exists precisely because those clients are counted, and a large share of them do not run JavaScript at all. Static HTML per route removes the question rather than optimising it.
02
The alternative to a framework is a hand-rolled server, and the honest cost of that is not the first thousand lines — it is routing, streaming, cache-control headers, asset hashing, image handling and content negotiation, each maintained forever by whoever wrote it. The parts specific to this site are written here: the Markdown twin at /services/<slug>/md is ours, and scripts/check-markdown.ts fails the build when negotiation and the route table disagree. The parts that are not specific to anyone were declined.
03
Static generation by default inverts the usual failure mode. In a render-everything-per-request architecture a slow data source turns into a slow page; here a page that has no server work performs no server work, so a failing source degrades the pages that read it rather than the site. The routes that must be dynamic — the census aggregate, the build identifier — are dynamic because they were marked as such, one at a time, not because the framework defaulted them there.
04
React 19 server components let data-fetching code sit in the same file as the markup that consumes it without shipping that code to the browser. This matters most on the boring pages: a service page that reads its own structured data and renders it adds nothing to the client bundle, which stays reserved for the few components that genuinely need motion or state.
05
The build is a place to enforce invariants, and this framework gives a build that fails. scripts/check-schema.ts breaks the deploy on a dangling @id in structured data, scripts/check-knowledge.ts breaks it on a corpus invariant, scripts/check-markdown.ts breaks it when a Markdown twin goes missing. On a hand-rolled static pipeline these checks would be a cron job that emails someone; here a broken knowledge graph never reaches production because there is no production build to carry it.
06
It is not the answer given to every problem. pakibus is a Vite build with three.js and a post-processing pipeline deployed to Cloudflare Workers, openclaw is a Docker-composed Node service running grammY and Baileys with no web front end at all, and mozg is a Turborepo with versioned Prisma migrations. Next.js is what gets reached for when the deliverable is a set of addressable pages with mixed staleness — DYD-main, status-kvo-app, rakairport and seahero are all that shape — and not when the deliverable is a render loop or a message consumer.
07
Vercel's hosting platform and Next.js are governed by the same company, which is a real coupling rather than a rumour. It is survivable here because the exit is legible: the framework runs on Node behind any reverse proxy, and the parts of this site that would be painful to move are the parts written here — negotiation, the check scripts, the census aggregation — not the parts the framework supplies. Naming the lock-in and measuring it is more useful than pretending a hand-rolled server has none.
An entry that cannot point at something you can open, run or read does not compile. That is a property of the type, not a promise in a paragraph.
Invariant
The framework moves faster than the code written against it. Caching defaults have changed meaning between major versions without the calling code changing, conventions have moved between routers, and the guidance shipped inside node_modules/next/dist/docs/ is more current than most published tutorials and most model training data — which means every upgrade starts with reading, not editing. On a site edited weekly that cost is absorbed. On a project built once, handed over, and touched twice a year by someone else, it is a liability: the client inherits a dependency that drifts out from under them, and there a plain static generator or a boring server-rendered stack is the more honest recommendation. Next.js also returns nothing when the deliverable is not a set of addressable pages — a render loop, a bot, a queue consumer — where it adds a build system and a routing model in exchange for no benefit at all.