WordloopWordloop
Decisions (ADRs)

Next.js with Server Components for the web app

We ship wordloop-app on Next.js Server Components rather than a client-side SPA to collapse AI-context data-fetch waterfalls.

0002 — Next.js with Server Components for wordloop-app

Status: Accepted Date: 2026-04-19 Deciders: app platform Supersedes:Superseded by:

Context

The Wordloop web app renders deeply nested AI-derived context: a Meeting contains TranscriptSegments, each segment has a speaker attribution (Person), the Meeting has a MeetingSynthesis with Topics and TalkingPoints, and a list of Tasks. Opening a Meeting is the single most common view in the product.

A client-side single-page application fetching this context produces a cascading waterfall. The client first fetches the Meeting, waits for the response, fetches the Transcription, waits, fetches Segments, waits, resolves Person records per speaker, waits, fetches the MeetingSynthesis, waits, fetches Tasks. Each hop is a full round trip between the browser and the edge — in practice, five to seven seconds of blank screen on a median connection before any meaningful content appears.

This is not a problem to optimise with skeleton screens or lazy loading. The waterfall is inherent to the data shape and the client-side fetch model.

Decision

Build wordloop-app on Next.js with the App Router and React Server Components. Meeting views, synthesis views, and the dashboard fetch their data on the server, close to the database, in a single request trip. The client receives the fully resolved DOM with content already present.

Client components remain where interactivity demands them: the live transcript stream, the editor, the command palette. These are bounded, named islands inside a server-rendered shell.

Consequences

Single round trip for the primary view. Opening a Meeting is one request from the browser; all downstream data fetches happen server-side in parallel, close to the database. Time-to-meaningful-paint drops from seconds to hundreds of milliseconds.

Database queries colocate with the code that needs them. A Server Component can query Postgres directly (through our Go API in practice, but the programming model is the same: the fetch happens where the latency cost is lowest).

Client bundles stay small. Components that never run on the client are never shipped to the client. The JavaScript bundle for the Meeting view is a fraction of what it would be in a pure-SPA architecture.

A sharper client/server boundary. Server Components cannot use useState, useEffect, or browser APIs. The boundary is explicit and enforced by the framework, which catches a common class of hydration bugs at build time.

Alternatives considered

  • Pure client-side React + Vite. Rejected for the waterfall problem described above. Viable only if the data shape were flat, which it is not.
  • Remix / TanStack Start / other RSC-capable frameworks. Considered equivalent in principle. Next.js chosen for the ecosystem maturity, the production track record of the App Router at our scale, and the team's existing expertise. Revisit if Next.js' direction diverges from our needs.
  • Hybrid: SPA shell + server-rendered HTML snippets. Rejected for the cognitive overhead of maintaining two rendering models. Server Components give us the same benefit with a single programming model.

Debt annotation

Principal: Moderate. The team has internalised the Server/Client Component boundary; new engineers spend their first week understanding when to use which.

Interest: Low to moderate. Next.js ships breaking changes in major versions; we pin and plan upgrades quarterly. The RSC model itself is stable.

Multiplier: Framework direction. If Next.js' architectural direction diverges materially from our needs, the cost of migrating is proportional to the size of the app. The Server Components abstraction is portable — Remix and TanStack Start implement the same conceptual model — so the migration risk is bounded.

Verification

  • Primary Meeting view renders meaningful content in a single round trip (observed in Core Web Vitals on production).
  • next build output shows Server Components are not included in client chunks.
  • No data-fetch waterfalls in the Network panel for the dashboard or Meeting view.

On this page