LOG 04 · FRAMEWORK

Next.js & Static Export

Next.js App Router compiled to a fully static site — output: 'export' — with no server process, no cold starts, and nothing left to patch at 2am.

Published · Updated

A build, not a server

This site — and most sites I ship — compile with `output: 'export'`: every route is generated to static HTML/CSS/JS at build time, with nothing running server-side afterward. That rules out server actions and on-demand rendering, but a portfolio or marketing site rarely needs them, and the trade gets something better in return — there's no server process that can go down, get overwhelmed, or need a security patch.

Dynamic-looking routes still work under static export via `generateStaticParams` — every possible path is enumerated and pre-rendered at build time instead of computed per-request, which is exactly how the individual entries in this journal are generated.

Metadata as code, not an afterthought

Next.js's typed metadata APIs — the `Metadata` export, `generateMetadata`, `sitemap.ts`, `robots.ts` — mean SEO configuration lives in the same TypeScript files as the page itself, checked by the compiler, instead of a separate CMS field that quietly drifts out of sync with what the page actually says.

App Router, TypeScript throughout

Every component on this site is a typed React component under the App Router — server components by default, client components only where interactivity (a WebGL canvas, a GSAP-driven effect, a subscribed piece of shared state) genuinely requires one. That distinction is deliberate: less JavaScript shipped to the browser for the parts of the page that never needed to hydrate in the first place.

The client-only trap

Static export makes one failure mode very easy to miss: anything a component mounts from an effect is absent from the exported HTML. This site's homepage sections are portal-mounted into a CSS3D scene once the 3D stage initialises, which works perfectly in a browser — and meant the page's <h1> existed only after hydration. Every crawler reading the static file saw a homepage with no heading at all.

The fix is not clever, just deliberate: anything that has to be in the HTML — headings, copy, links, structured data — is rendered outside the client-only tree, and the interactive layer is left to do the part it is actually there for. It is worth opening the built output and reading it, rather than trusting the dev server, because those two are not the same document.

Services that use this