Field note / patterns

publishedupdated 2026-05-04#frontend#patterns#react#nextjs#architecture

Frontend Patterns

Senior and principal-level frontend patterns for shell composition, state boundaries, rendering ownership, and multi-team platform UI systems.

TL;DR

  • Good frontend architecture is mostly about boundaries: ownership, rendering, data flow, and failure isolation.
  • I optimize for patterns that let product teams move independently without turning the UI into a bundle of disconnected decisions.
  • At principal level, the key question becomes: can many teams keep shipping for years without central UI infrastructure becoming the new bottleneck?
  • The best frontend patterns feel boring under load: stable contracts, resilient shells, and explicit state ownership.

Context

Frontend systems get hard long before they get big in lines of code. They get hard when many teams need to ship independently, when performance matters, and when the UI becomes a platform instead of a single app.

The patterns I care about most come from building React and Next.js systems where multiple teams share infrastructure but should not block each other every week.

The Approach

1. Shell + feature composition

I like a thin shell that owns:

  • routing
  • auth/session bootstrap
  • design system primitives
  • analytics and observability hooks
  • shared error boundaries

Feature teams own their domains behind those seams.

I keep the shell small and stable, and let feature slices evolve independently behind it.

2. Rendering ownership should be explicit

In modern Next.js, I default to server-rendered content and use client-owned state only where the browser truly needs interaction authority.

That usually keeps:

  • bundle size lower
  • time-to-content faster
  • data fetching closer to where secrets and infrastructure already exist

3. Local state first, shared state intentionally

I start with local component state, then lift state only when multiple branches truly need coordination. I treat global state as infrastructure, not as the default.

Useful escalation path:

  1. local state
  2. lifted state
  3. reducer
  4. scoped context
  5. external store

4. UI state and server state should not blur together

I do not want loading, cache, and retry concerns tangled up with every dialog toggle and checkbox flag. Even without a dedicated data library, I separate:

  • server truth
  • optimistic local intent
  • presentation-only state

5. Explicit resilience boundaries

At principal level, a frontend is not “working” just because the happy path renders. I want:

  • loading boundaries
  • error boundaries
  • retry paths
  • degraded mode behavior
  • skeletons or partial renders where appropriate

If one downstream dependency is slow, the whole page should not collapse by default.

6. Shared UI infrastructure should stay intentionally thin

The shell, design system, telemetry layer, and auth bootstrap are powerful shared assets. They also tend to become accidental monoliths if every team pushes local concerns into them.

My rule is simple:

  • if it helps almost every route, it can live in shared infrastructure
  • if it helps one feature team today, it probably belongs in that feature

Trade-offs

  • More modularity improves team autonomy, but weak design-system and contract discipline can fragment UX quickly.
  • Server-first rendering improves page weight and content speed, but it forces better thinking about where state and effects belong.
  • Shared infrastructure reduces duplication, but a bloated shell becomes a platform tax on every team.
  • Micro-frontends can help organizational scaling, but they are the wrong answer if one cohesive app and one team can solve the problem more simply.

References