Rendering & Data Architecture
(How frontend systems actually move data and pixels)
Architect rule:
Rendering and data flow are not implementation details.
They are core architectural decisions.
Before You Read
This part is framework-agnostic in principle, but some examples reference React and Next.js because modern server/client boundaries, hydration, and streaming are easier to discuss concretely there. Read those examples as implementations of broader architectural ideas, not as the only valid path.
The Rendering Spectrum
1.1 The False CSR vs SSR Debate
Many frontend discussions still frame rendering as a choice between CSR and SSR. That framing is usually too coarse to help with a real architecture decision.
Modern frontend rendering exists on a spectrum. A single product may mix static generation, cached server rendering, streaming, client-rendered islands, and fully interactive client surfaces at the same time.
1.2 The Full Rendering Matrix
| Model | What it is | Usually strongest when... | Common cost |
|---|---|---|---|
| CSR | HTML shell plus client-side rendering | interaction dominates and SEO is secondary | more shipped JS and more client execution |
| SSR | HTML generated per request | personalization or freshness matters on first paint | hydration and server compute cost |
| SSG | HTML generated at build time | content is stable and broadly cacheable | slower content refresh without revalidation strategy |
| ISR / revalidation | prebuilt output refreshed over time | content changes on a predictable cadence | cache invalidation complexity |
| Streaming SSR | response delivered in chunks | page size or dependency latency would delay first paint | more moving parts in error and loading states |
| Partial hydration / islands | hydrate only interactive regions | large pages with limited interactivity | additional composition complexity |
Architect insight:
The question is rarely "Which rendering model does the app use?" The better question is "Which rendering costs are acceptable for this route or segment?"
1.3 Rendering Is a Cost Model
Every rendering choice changes a cost profile:
- server compute cost
- client execution cost
- hydration cost
- cache complexity
- operational complexity
- correctness and freshness risk
Architects do not choose a rendering mode because it is fashionable. They choose it because the trade-offs fit the route.
Choosing Rendering Strategy
What This Chapter Helps You Decide
This chapter helps you decide:
- which routes should be static, server-rendered, streamed, or mostly client-rendered
- where hydration is worth paying for
- when a mixed route is better than a global rule
2.1 Questions to Answer Before Choosing
Before choosing a rendering strategy, answer these questions:
- Is SEO or discoverability material for this route?
- What must be visible above the fold on first paint?
- What must be interactive immediately?
- How fresh does the data need to be?
- Who is paying the compute cost: client, edge, or origin?
- What device and network profile do you need to support?
If those answers are fuzzy, the rendering decision is still immature.
2.2 Route-Level Rendering Decisions
Rendering strategy is usually chosen per route or page segment, not for the whole application.
| Route or surface | Likely strategy | Why |
|---|---|---|
| marketing page | SSG or cached SSR | fast first paint and strong cacheability |
| account login or auth callback | SSR or thin client surface | correctness and security matter more than raw cacheability |
| dashboard workspace | mixed route: server-rendered shell plus interactive client regions | high interactivity with selective hydration |
| admin report | cached SSR or streaming SSR | large data with a strong first-load requirement |
| live trading or collaboration panel | mostly client-driven with explicit real-time strategy | interaction and local responsiveness dominate |
2.3 The Hidden Cost of Hydration
Hydration is not free polish after SSR. It is real client work:
- component tree replay
- event binding
- JavaScript execution on the main thread
- reconciliation against already visible markup
SSR without a hydration strategy can still deliver a slow-feeling page. The right comparison is not "SSR good, CSR bad." The right comparison is "Which combination of server work and client work gives users the best outcome on this route?"
2.4 Rendering Decision Matrix
| Signal | Lean toward more server work when... | Lean toward more client work when... |
|---|---|---|
| SEO | route must be indexable | route is private or not search-sensitive |
| first paint | useful content must appear immediately | shell can load first and UI can progressively initialize |
| interaction density | interactive regions are limited | interaction is continuous and highly stateful |
| freshness | server can cheaply orchestrate and cache | freshness is user-driven and local |
| device quality | low-end devices are common | audience devices are strong and app is power-user oriented |
Server and Client Boundaries
3.1 Framework-Specific Note
In React-heavy systems, Server Components and Client Components make the server/client boundary explicit. In other ecosystems the same idea may appear as islands architecture, resumability, or conventional server templating plus client enhancement.
The implementation differs. The architectural question does not:
Which work belongs on the server, and which work truly needs client runtime state and interaction?
3.2 What Server-Oriented Composition Buys You
Server-oriented composition can reduce:
- shipped JavaScript
- client waterfalls
- duplicated data orchestration
- accidental exposure of backend-only logic
It also introduces constraints:
- serialization boundaries
- tighter coupling to platform capabilities
- new debugging and local-development complexity
3.3 Common Boundary Mistakes
Avoid these defaults:
- making every component interactive "just in case"
- passing large serialized trees to the client when a smaller interaction payload would do
- embedding business rules in presentational client components
- assuming server-side composition removes the need for caching and error design
3.4 React Compiler and Client Optimization
In React-heavy systems, the React Compiler changes how aggressively teams need to reach for defensive memoization. That does not remove the need for good boundaries.
The strongest default remains:
- optimize state flow before micro-optimizing components
- shrink the client surface before sprinkling manual memoization
- treat compiler adoption as a build and tooling decision, not a substitute for architecture
Data Architecture: Ownership Before Tools
4.1 The Core Data Question
Every meaningful piece of data should answer:
Who owns this data, and where is the source of truth?
Architects design ownership maps before choosing libraries.
4.2 The Four Data Domains
| Domain | Characteristics | Typical examples | Default architectural home |
|---|---|---|---|
| Server-owned data | authoritative, persisted, shared | user profile, orders, permissions | server fetch layer plus explicit cache policy |
| Client UI state | ephemeral, local, interaction-driven | modal visibility, temporary selection | component or feature-local state |
| URL state | shareable, durable, navigable | filters, pagination, search query | router and URL |
| Global client state | cross-cutting runtime state | auth session snapshot, theme, feature flags | narrowly scoped global store or shell state |
4.3 Nuance That Keeps This Honest
Use these as defaults, not dogma.
- Server-owned data should rarely be copied into long-lived client state unless offline behavior, local editing, or conflict resolution truly requires it.
- UI state should usually stay local, but shell-wide overlays, multi-panel workspaces, and collaborative experiences may need broader coordination.
- URL state belongs in the URL when users expect navigation, sharing, or restoration semantics.
- Global state should be treated as expensive shared infrastructure, not forbidden territory.
4.4 State Ownership Worksheet
For each stateful value, ask:
- Who is authoritative?
- What invalidates it?
- Should the back button restore it?
- What breaks if it is stale?
- Does more than one route or shell-level surface depend on it?
If the answer set is unclear, the architecture is still ambiguous.
Caching as Architecture
5.1 Cache Is a Contract
Cache is not merely a speed trick. It is an explicit agreement about freshness, consistency, and failure.
Architects define:
- acceptable staleness
- invalidation triggers
- recovery behavior on mismatch
- ownership of each cache layer
5.2 Multi-Layer Cache Model
| Layer | Typical owner | Main question |
|---|---|---|
| CDN / edge cache | platform or ops | what can be shared broadly and safely? |
| Browser HTTP cache | protocol and server headers | how should repeat navigation behave? |
| Framework or server cache | application | what can be reused across requests or revalidation windows? |
| In-memory client cache | runtime | what improves interaction without hiding stale data? |
Architects decide which layer owns freshness instead of letting caches compete accidentally.
5.3 Invalidation Strategies
Architecturally credible options include:
- time-based revalidation
- event-based invalidation
- user-triggered refresh
- background refresh with visible stale state
The anti-pattern is not a specific library. The anti-pattern is pretending invalidation does not exist.
Frontend-Backend Contract Architecture
6.1 API Contracts Are Part of Frontend Architecture
Frontend systems do not only consume APIs. They encode assumptions about shape, latency, permissions, field availability, and backward compatibility.
That means contracts are architectural, not incidental.
6.2 What Good Contract Design Includes
Architects should define:
- ownership of schema changes
- versioning and deprecation rules
- fallback behavior when fields are absent or delayed
- error shape expectations
- whether the UI depends on a generic backend API or a UI-oriented aggregation layer
6.3 BFF Boundaries
A Backend-for-Frontend becomes compelling when it:
- removes client waterfalls
- shapes data to match route needs
- isolates the UI from backend churn
- centralizes policy, validation, or orchestration logic
A BFF becomes harmful when it duplicates business logic without clear ownership or becomes an unbounded dumping ground.
6.4 Testing Contract Assumptions
At minimum, test:
- schema compatibility for critical fields
- behavior when optional data is missing
- error states for partial and malformed responses
- upgrade paths when a contract changes
This is where integration and contract tests add more architectural safety than large numbers of shallow unit tests.
State Consistency and Testing
7.1 The Myth of Perfect Consistency
Frontend systems are distributed systems. Between browser, network, cache, and backend, perfect synchronicity is rarely realistic.
Architects design for:
- eventual consistency
- clear stale states
- visible uncertainty where it matters
- explicit rollback behavior
7.2 Optimistic vs Pessimistic Updates
| Strategy | Usually appropriate when... | Main risk |
|---|---|---|
| Optimistic update | action is reversible and UX speed matters | rollback complexity and trust loss if failure is poorly handled |
| Pessimistic update | action is high-risk or high-value | slower-feeling UX and more waiting states |
Architects choose this per action, not once per application.
7.3 Testing the Data Architecture
For critical user flows, verify:
- route rendering choice is intentional
- caches invalidate predictably
- loading states are consistent across slow and failed responses
- optimistic flows roll back clearly
- partial data does not collapse the whole route
Review Checklist
Use this checklist during review:
- Does each route have a declared rendering strategy?
- Is the hydration surface smaller than the visible page surface?
- Is every important data domain assigned to an owner?
- Are cache layers intentional rather than accidental?
- Can the UI survive partial or late data?
- Are API contract assumptions tested somewhere real?
Exercises
Exercise 1 - Route Rendering Matrix
Pick your current product and create a table with:
- route or segment
- rendering strategy
- hydration scope
- freshness requirement
- cache owner
Exercise 2 - Data Ownership Map
Map the ten most important pieces of data in your product and label:
- owner
- storage location
- invalidation rule
- failure mode
Exercise 3 - Contract Failure Drill
Take one critical endpoint and simulate:
- a missing optional field
- a delayed response
- a partial failure
- a backward-incompatible change
Document what breaks first.
Further Reading
- MDN: JavaScript event loop - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop
- MDN: How browsers work - https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work
- React docs: Server Components - https://react.dev/reference/rsc/server-components
- React docs: memo - https://react.dev/reference/react/memo
- Next.js docs: Rendering - https://nextjs.org/docs/app/building-your-application/rendering