ZJH

Designing Experience Boundaries: A Modern Frontend Architecture Guide

A practical frontend architecture guide covering rendering modes, components, state, data loading, performance, and delivery.

Frontend engineering is often reduced to “choose a framework and write components.” In real projects, the frontend is not just the screen. It is the interaction system that lets users complete work.

A mature frontend architecture has to handle:

  • Page organization.
  • Data loading and caching.
  • State ownership.
  • Component reuse and constraints.
  • Loading, empty, and error states.
  • Performance over time.
  • Collaboration with backend, design, testing, and deployment.

This article is a practical guide to those decisions.

What Frontend Architecture Solves

The frontend does not merely paint backend data. It turns system state into a continuous user experience.

When a user clicks a button, the frontend needs to answer:

  • Is this action available?
  • What happened after the click?
  • How can the user recover from failure?
  • Was the data actually updated?
  • Will state survive refresh or navigation?
  • Does this still work on small screens, slow networks, and older devices?

The center of frontend architecture is not the component library. It is the experience loop.

The Evolution Of Frontend Architecture

Multi-Page Applications

Multi-page applications return complete HTML pages from the server. Each navigation loads a new page.

This model is still useful:

  • It is simple.
  • First load is stable.
  • SEO is natural.
  • Content-heavy products can move quickly.

It fits content sites, documentation, simple admin tools, and low-interaction pages.

SPAs

Single-page applications move routing, state, and rendering into the browser. The backend provides APIs. The frontend owns the application experience.

Benefits:

  • Smooth interaction.
  • Complex client state.
  • Clearer frontend-backend collaboration.
  • Better authenticated application flows.

Costs:

  • First load can be slower.
  • JavaScript bundles can grow quickly.
  • Data fetching, permissions, caching, and error states need explicit design.

SSR And SSG

Server-side rendering generates HTML per request. Static site generation generates HTML at build time.

They solve different problems:

  • SSR is useful for personalization, SEO, and dynamic data.
  • SSG is useful for content, docs, blogs, and marketing pages.

Modern frameworks often support mixed rendering. A single site can statically generate content, render detail pages on the server, and hydrate authenticated dashboards on the client.

Server Components And Islands

One modern frontend direction is reducing unnecessary client JavaScript.

React Server Components let some components run only on the server and send rendered results to the client. Astro Islands start with static HTML and hydrate only interactive regions.

Both ideas share the same principle: do not put everything in the browser.

A Default Frontend Layering

A maintainable frontend can start with these layers:

routes / pages
  -> route and layout composition

features
  -> business workflows: lists, forms, detail views, actions

components
  -> shared UI and presentational components

data
  -> API clients, query hooks, caching rules

state
  -> local state, URL state, shared state

styles / design tokens
  -> visual variables and layout rules

Not every project needs strict folders. Every project needs to avoid mixing pages, business workflows, reusable components, requests, and state into the same files.

Routes And Pages

Routes are not just file paths. They express how users enter and move through the product.

Design routes around tasks:

/notes
/notes/:slug
/projects
/projects/:slug
/settings/profile
/settings/billing

Page files should compose. They should not contain every API call, validation rule, permission check, state transition, and large component tree.

Data Loading And Cache

Frontend data starts with three categories.

Server State

Server state comes from the backend: users, projects, orders, permissions. The source of truth is the server.

Common handling:

  • Load through framework data APIs or request utilities.
  • Use a query cache for loading, errors, and invalidation.
  • Invalidate or update related queries after mutations.

Client State

Client state only exists in the browser: modal state, active tab, temporary form draft.

Do not make it global too early. Keep it local when possible. Put it in the URL when it should be shareable.

URL State

Filters, pagination, and search terms often belong in the URL:

/projects?status=active&page=2&q=search

This supports refresh, back and forward navigation, link sharing, and debugging.

Choosing State Management

Do not start with a state library. Start with state lifetime.

Component-local state
  -> useState / signals / local state

Shared but feature-local state
  -> context / feature store

Server data
  -> query cache / framework data APIs

URL-expressible state
  -> search params / route params

Complex editors or canvases
  -> state machine or domain store

Many global-store problems come from copying server state into client state and then trying to keep two truths synchronized.

Component Design

Components usually fall into three groups.

Foundation Components

Buttons, inputs, dialogs, menus, table cells. They should be stable, accessible, and composable.

Business Components

Project cards, order status panels, permission widgets. They understand business fields but should not own entire workflows.

Page Components

Pages compose layout, data, and business components. The thinner the page, the easier it is to evolve.

Good component boundaries have two traits:

  • You can understand how to use the component without reading the whole project.
  • The implementation can change without forcing many callers to change.

Forms And Errors

Forms concentrate frontend complexity.

Real forms handle:

  • Initial values.
  • Synchronous validation.
  • Async validation.
  • Submitting state.
  • Server errors.
  • Optimistic updates or post-submit refresh.
  • Navigation warnings for unsaved changes.

Errors should not all become alert(error.message).

Separate:

  • Field errors near fields.
  • Form errors near the form.
  • Permission errors with a path to sign in or request access.
  • Network errors with retry.
  • System errors with logging and understandable copy.

Performance Budget

Frontend performance should be an architectural constraint, not a last-minute compression task.

Watch:

  • How quickly useful HTML arrives.
  • Whether critical CSS and fonts block rendering.
  • Whether JavaScript bundles are growing.
  • Whether images have correct dimensions and formats.
  • Whether long lists need virtualization.
  • Whether the main thread blocks interaction.

Common strategies:

  • Generate content pages statically.
  • Avoid hydrating non-interactive regions.
  • Lazy-load heavy components.
  • Serve correctly sized images.
  • Keep admin-only components out of public pages.

Working With The Backend

Frontend-backend collaboration should not depend on memory or chat messages.

Agree on:

  • Request fields.
  • Response fields.
  • Error structures.
  • Authentication.
  • Pagination and sorting.
  • Time formats.
  • Nullability rules.

OpenAPI, GraphQL schemas, tRPC, or shared TypeScript types can all work. The tool matters less than making interface changes discoverable before deployment.

From Zero To Production

A real frontend project can move like this:

  1. Define the page map and core user paths.
  2. Choose rendering mode: static, server, client, or hybrid.
  3. Build layout, design tokens, and common components.
  4. Define API contracts and error format.
  5. Create the smallest working loop: list, detail, create, edit.
  6. Add loading, empty, error, and permission states.
  7. Add search, filters, and pagination.
  8. Check performance and accessibility.
  9. Connect CI, preview deployment, and production deployment.

The frontend is not a UI layer added at the end. It participates in system boundary design from the beginning.

My Defaults

For personal sites and content sites: Astro or Next.js with static generation and minimal interactive islands.

For admin systems: React or Vue are both fine. Focus on forms, permissions, tables, data fetching, and a consistent design system.

For SaaS products: choose a framework that can support SSR, SSG, and CSR together. Design auth, errors, cache, logging, and analytics early.

For internal tools: avoid over-engineering. Fast delivery, clear permissions, and stable deployment matter more than framework novelty.

Further Reading