Skip to content

Front end

Next.js

The React framework we use when a site needs server rendering, static export or file-based routing rather than a bare client bundle.

Next.js is a framework wrapped around React that supplies the parts React leaves out: routing from the file system, a build pipeline, server rendering, static export and a set of conventions about where code runs. The App Router pushed most of that towards components that execute on the server by default, with client components as the exception you opt into. That default is the whole reason to pick it, and also the source of most of the confusion.

How we use it

We use it in three quite different modes. As a fully static export it produces plain HTML with no server at all, which is what Linked Corporate ships: eight pages, a filtered gallery, JSON-LD for the company record, no backend and no database, hosted as files. That site takes enquiries through a chat link rather than a form, an argument we set out in why a chat link beats a contact form.

At the other end, DMARC Engine and Jonosakti run Next.js on Cloudflare Workers through OpenNext, so the same App Router codebase serves the marketing pages, the signed-in application and the API routes from one deployment, with D1, KV and R2 available as bindings inside it. @Heartbeat takes the middle road, a Next.js front end deployed separately from a Worker API on its own subdomain, which keeps the two halves independently deployable at the cost of a CORS configuration. Starterflare pairs a small Next.js interface with a NestJS backend, and the AHZ Associates sites serve Next.js build output across a UK site, a global site and an application portal, a spread we wrote about in one company, sixteen countries.

What it costs you

The release pace is punishing. We have live builds on 14, 15 and 16, and the gap between 14 and 16 is not a bump: caching defaults changed, async request APIs changed, and a major upgrade on a real application is a week with a test plan, not an afternoon.

Running it on Workers adds a translation layer. OpenNext maps the Next.js server onto the Workers runtime, and it is good, but it is a compatibility layer: you inherit its lag behind Next.js releases, its Node compatibility flags, and a bundle that counts against the Worker script size limit. When something breaks at that seam, you are debugging two projects at once.

The caching model is the other tax. Between the router cache, the data cache and full route caching, the question of whether a given page is fresh has a longer answer than it should, and the honest answer on most projects is that somebody has sprinkled revalidate until the symptoms stopped. Static export, for its part, disables the good half of the framework: no route handlers, no image optimisation, no dynamic rendering.

When we would choose something else

For an application that lives entirely behind a login, server rendering earns nothing. There is no crawler to serve, the first screen is a login form, and the framework's whole advantage is spent on complexity you still have to operate. ClassProfile and Taxcare are both plain Vite single page applications for that reason, and they build in seconds.

If the site is content rather than software, a generator that emits HTML and ships no framework runtime is a better fit than a static Next.js export, and the delivery side of that is covered on hosting and delivery. Where the backend is the substance and the interface is thin, we would rather build the API properly with Hono or NestJS and keep the front end small.

Where we have used it

Every build below lists this in its stack, so the claim is checkable.