From Next.js to SSG: Rebuilding tradik.com
tradik.com ran on Next.js with output: 'export'. That produced a static site,
and everything about it was reasonable in isolation. Taken together it was a
React toolchain, a node_modules tree of several hundred packages and a build
step, all to publish nine pages that do not change between deploys.
The output was static. Nothing producing it was.
It now runs on SSG, the static site generator we maintain — a single Go binary. Markdown and YAML in, HTML out.
What the framework was actually buying us
Very little, on this site. Next.js earns its keep when you have routing that depends on data, incremental regeneration, or server components doing real work. tradik.com has a homepage, four marketing pages, three legal pages and a contact form. The React runtime shipped to every visitor existed to hydrate components whose output never varied.
The parts that did vary — the service catalogue, the product cards, the tool
categories, the FAQ — were TypeScript arrays inside .tsx files. Editing a
product tagline meant editing a React component. That is a strange place to keep
copy.
What replaced it
Content is Markdown with frontmatter. The structured parts moved to YAML in
data/, and the templates read both.
That split turned out to matter more than the file formats. The homepage FAQ and
the FAQPage structured data behind it now come from the same six entries in
data/faq.yaml. Before, they were a JSX block and a separate array passed to a
<FAQSchema> component — two lists that had to be kept in step by hand, and the
kind of thing that silently drifts until a rich result advertises an answer the
page no longer shows. The same is true of the product cards and their
ItemList.
The build says no
The interesting difference is not the generator. It is that the build now enforces what used to be a review comment:
check_links: strict # a dead internal link fails the build
check_images: strict # an image with no alt attribute fails the build
check_meta: warn # title and description length, advisory
check_orphans: warn # pages nothing links to
check_links caught a stale /blog/ reference in the footer within a minute of
being switched on. check_orphans caught the tag archives the generator creates
automatically and that nothing linked to — pages a crawler would find and a
reader never would.
What a visitor gets
No framework, no hydration, no render-blocking JavaScript. The only script is a few kilobytes of progressive enhancement for the mobile menu and the contact form, and the site works with it switched off: the form posts normally and a Cloudflare Pages Function answers, redirecting to a real confirmation page rather than to a query string only a script could read.
What we kept
Every URL. The old export served /services; the build now serves /services/,
and Cloudflare answers the extension-less form with a redirect, so nothing that
was linked or indexed has moved. The pre-2019 WordPress paths that still attract
crawlers — /press-room, /category/*, the dated archives — are answered
explicitly rather than left to 404.
What it cost
A day, most of it spent on content rather than code, and two bugs found in our own generator along the way — both fixed in 1.8.21 before this site shipped. Dogfooding works.