Files
cairnquire/.project/adrs/adr-003-ssr-strategy.md

2.6 KiB

ADR-003: SSR Strategy - Go Templates with Preact Hydration

Status

Accepted

Context

The application needs to serve document pages that are readable without JavaScript (for performance and accessibility), while providing rich interactivity (comments, editing, search) when JavaScript is available. We need a server-side rendering strategy that works with our Go backend and Preact frontend.

Decision

Strategy C: Go's html/template renders initial HTML for all pages. Preact hydrates interactive components on the client.

Implementation Details

  1. Server Rendering:

    • Go handlers query database for document content
    • Markdown parsed to HTML server-side (Goldmark)
    • html/template renders full page with document HTML embedded
    • No JavaScript required for reading
  2. Client Hydration:

    • Preact components mount into designated DOM containers
    • Comments, search, editor are Preact islands
    • Static content remains server-rendered HTML
    • preact-iso handles client-side navigation between pages
  3. Data Passing:

    • Initial state embedded as JSON in <script type="application/json"> tags
    • Preact reads initial state during hydration, avoids extra API calls
    • CSP nonce applied to all inline scripts

Consequences

Positive

  • First contentful paint is instant (pure HTML)
  • SEO-friendly without extra complexity
  • Graceful degradation: works without JS
  • Go templates are auto-escaping XSS-safe
  • No V8/JS runtime embedded in Go binary
  • Smaller client bundle (only interactive components)

Negative

  • Some code duplication: markdown parsing logic in both Go and JS (for preview)
  • Template syntax is verbose compared to JSX
  • Hydration can cause flicker if not carefully managed
  • Client state must reconcile with server-rendered HTML

Alternatives Considered

Go Templates Only (No Preact)

  • Pros: Zero client JS, maximum performance
  • Cons: No interactive features (comments, real-time sync, search)
  • Rejected: Doesn't meet collaboration requirements

Embedded JS Runtime (QuickJS/V8)

  • Pros: True isomorphic rendering, JSX templates
  • Cons: Adds C dependency, complex build, larger binary, security surface area
  • Rejected: Violates minimal dependency principle

Preact SSR via WASM

  • Pros: Same templates on server and client
  • Cons: WASM overhead, complex build pipeline, slower than Go templates
  • Rejected: Go templates are faster and simpler

References