ADR 0402: Site layouts use server-rendered React components
Amendment (2026-09-17) — the docs shell is the package's. With design-system 0.35.0 the document corpora consume the package Docs layout and its drawer behavior, the static Search palette with its list, empty, and status adapters, the Table of contents with authored numbers, the Anchor heading for model-derived headings, and the Docs header's own width token. The site keeps no layout, drawer, or contents renderer of its own; page-owned script remains for the search lifecycle, copy controls, navigation position, and the contents scroll spy. The Markdown decorator restates the Anchor heading anatomy, held to the component's output by a parity test, because the string renderer cannot wrap arbitrary heading markup in the component.
Amendment (2026-09-16) — the document corpora converted. Manual and decision pages render through
DocumentLayoutand page components undersite/ui/. The corpus model and body renderer insite/docs.tsxstay React-free: the site route inventory evaluates that module during codegen, whose permissions exclude theNODE_ENVreadreact-domperforms on load.site/documents.tsxis the request handler where React joins the model. Package components the static, progressive-enhancement site cannot consume as-is — the hydrated Search palette and the self-numbering Table of contents — are consumed up to their boundary and the gap is named there, not recreated locally.
Status: accepted on 2026-09-15. Amends ADR 0135, ADR 0139, ADR 0205, ADR 0279, and ADR 0287.
Context
§The site's owner works fluently in React. Build-time marketing pages, string-based document shells, and a separate release renderer make the same layout change require different techniques. Repeating package component anatomy in strings also prevents an upstream component fix from reaching the consumer through a dependency update.
The production server's React exclusion forces this split even though the site already depends on React for authoring. Release comparisons depend on a request's version parameter. A pre-rendered page cannot express every comparison without another rendering mechanism or browser computation.
Decision
§HTML-producing site modules use .tsx. React pages, shared layouts, and site adapters live under site/ui/; routing, content models, text projections, and build orchestration remain TypeScript. The shared Document renders complete HTML for both build-time marketing output and request-time release comparisons. MarketingLayout owns their shared header, footer, and landmarks; the navigation destinations are centralized in site/navigation.ts. Page components consume the exact published design-system React adapters; they do not reconstruct package classes or component anatomy.
React is allowed in the website server. Browser hydration is a separate decision: the current output uses HTML, CSS, package enhancements, and page-owned JavaScript. React effects and event handlers do not execute in that output. The terminal engine's dependency and process boundaries remain independent.
Markdown stays the content authority. Release notes use the package Markdown component, with one site adapter adjusting heading depth and anchor scope until those controls are available upstream. Its serialized output and the authored theme bootstrap pass through one explicit HTML-insertion component. The document corpora retain their own TSX shell and workflow/glossary projections; their component conversion must preserve raw editions, source semantics, navigation, and search.
The route inventory derives from the marketing registry, document models, and fixed endpoint registry. Serving, the sitemap, and the generated atlas consume that inventory. Publishing a document does not require adding its URL to a second list.
Consequences
§- Page authors navigate by component and layout, and package markup updates reach consumers through the published adapters.
- React adds dependencies and rendering work to the website server. Request validation and caching remain server responsibilities; the installed CLI gains no React requirement from this decision.
- A page using
useEffect, React state transitions, or React event handlers needs an explicit browser entrypoint and hydration. Merely adding those hooks to a server component does not activate them. - Structural tests enroll future site sources in the TSX convention and reject package-class reconstruction in the shared UI. Existing corpus renderers retain their document contracts while they move to components.
- The existing exact package pin, local asset emission, content negotiation, and browser accessibility obligations remain in force.
Alternatives considered
§Keeping React build-only would require pre-rendering documents and retaining a separate solution for query-dependent releases. A complete client application framework could add hydration and Fast Refresh, but it would couple the authoring cleanup to a browser-runtime decision for which no current page requires React.