Ranveer KumarBlog
Frontend Architecture22 min read

What Senior Frontend Engineers Are Expected to Design

A technical deep-dive into the five architecture dimensions senior frontend engineers must master: state architecture, rendering strategy, performance budgets, frontend scalability, and design systems.

Updated Jun 3, 2026

established the core premise: React fluency is a prerequisite, not a differentiator. What senior frontend engineers are evaluated on is their ability to design systems — not just components.

This article unpacks what that means in practice. The five dimensions below map directly to what senior engineers, UI architects, and engineering leaders are expected to reason about, design, and own in any significant frontend product.

These are not neat academic categories. They bleed into each other constantly. A rendering strategy decision affects state ownership. A state normalization choice affects caching. A design system token model affects accessibility. Good frontend architecture requires holding all five in tension simultaneously.

1. State Architecture

The junior question vs. the senior question

A junior engineer asks: where do I put this value?

A senior engineer asks: who owns this state, how long does it live, who can mutate it, how does it stay consistent, and what breaks when it changes?

State management is one of the most consequential and most commonly mismanaged decisions in frontend architecture. The cost of wrong state ownership does not show up in the first sprint. It shows up as duplicated server calls, stale UI, race conditions, and navigation bugs discovered six months after the feature shipped.

The taxonomy matters

There are at least five meaningfully distinct kinds of state in a frontend application, and each has a correct home.

Local state belongs to a single component and dies when the component unmounts. React's useState and useReducer are the right tools. Do not hoist local state unless there is a concrete reason to share it. Premature hoisting is one of the most common sources of unnecessary re-renders and coupling.

Lifted state is local state that has been moved up the tree to share it across siblings. This is appropriate when two sibling components have a common parent that is close in the tree. When the common parent is too far up, lifted state becomes global state in disguise — and global state has higher coordination cost.

URL state belongs in the browser address bar. Filter values, sort order, search queries, tab selection, and pagination cursors that affect the shareable view of a page should live in the URL. URL state survives page refresh, enables deep-linking, and cooperates with browser back and forward navigation. Forgetting URL state is one of the most consistent product experience failures in data-intensive UIs.

Server state is data that originates on the server and is fetched, cached, and synchronized by the client. Libraries like TanStack Query and SWR are designed specifically for this: they manage loading, error, refetch, cache invalidation, and background sync without the client needing to own the underlying data. Server state is not global client state. Treating it as global state creates duplication, divergence, and cache invalidation problems.

Global client state is state that multiple parts of the UI need to read or write, that does not belong to the server, and that does not belong in the URL. Auth state, theme selection, feature flags, and notification queues are candidates. Global client state should be small, explicitly typed, and treated as a last resort when other options are not appropriate.

// State ownership example: explicit typing prevents accidental blending
// of server state and client state into one store.

type AuthState = {
  userId: string | null;
  role: "viewer" | "editor" | "admin" | null;
  isAuthenticated: boolean;
};

type UIState = {
  sidebarOpen: boolean;
  activeTheme: "light" | "dark" | "system";
};

// Server state lives in TanStack Query or SWR, not in this store.
// AuthState and UIState are global client state — small, explicit, typed.

Common mistakes

The most common state mistakes at scale are:

Treating server state as global client state. When API responses are stored in Redux or Zustand instead of a server-state library, teams end up writing manual cache invalidation logic that diverges from server truth. This produces stale UIs that are hard to debug.

Forgetting URL state. Tab selections, filters, and search terms that are kept in React state get lost on page refresh, break sharing, and create support tickets.

Lifting state too early. Premature hoisting creates unnecessary coupling. Before lifting state, ask whether the sharing requirement is real or speculative.

Normalizing data too late. When arrays of entities are stored without normalization, updating a single entity requires iterating the full list. At scale, this creates performance problems and consistency bugs.

State Decision Matrix

State typeOwnershipBest storage locationRisk if mishandled
UI ephemeral (open/close, hover)Single componentuseState in componentNone — contained
Shared sibling stateNearest common parentLifted useStateCoupling if parent is too far up
URL-serializable stateBrowserURL query paramsDeep-link breakage, navigation regression
Server dataBackendTanStack Query / SWR cacheStale UI, duplicate fetches, manual invalidation
Global UI stateApplicationLightweight store (Zustand, Context)Unexpected re-renders if not scoped
Workflow stateFeature moduleXState / local state machineRace conditions, undefined transitions

Senior interview signal

Strong candidates describe state not by which library they prefer, but by the classification problem. They distinguish server state from client state. They default to local state and explain what specific requirement pushes state higher. They design cache invalidation strategies, not just fetching logic.

State ownership boundariesFive state zones showing local, lifted, URL, server, and global state with their correct homes.LocaluseStateSingle componentdies on unmountPrefer this firstLiftedCommon parentShared siblingsavoid over-liftingURL stateQuery paramsFilters, sort, pageshareable, bookmarkableServer stateQuery cacheAPI data, freshnessinvalidation rulesGlobal clientStore / ContextAuth, theme, flagsuse sparinglyLast resort
State Ownership BoundariesEach state type has a natural home. Crossing these boundaries without a clear reason creates coupling, stale UI, and cache invalidation debt.

2. Rendering Strategy

The junior question vs. the senior question

A junior engineer asks: should I use getServerSideProps or useEffect for this fetch?

A senior engineer asks: what does this route need — static HTML, server-rendered personalized content, client-rendered interactivity, or a streamed partial response — and why does that choice serve the user and the performance budget?

Rendering strategy is one of the highest-leverage decisions in frontend architecture because it affects first-paint performance, SEO visibility, hydration cost, caching behavior, deployment infrastructure, and content freshness simultaneously.

The rendering spectrum

Client-Side Rendering (CSR) delivers a minimal HTML shell and executes all rendering in the browser. CSR is appropriate for highly interactive, personalized, authenticated applications where SEO is irrelevant. The trade-off is a blank initial state that depends on JavaScript loading and executing before users see meaningful content.

Server-Side Rendering (SSR) generates HTML per request on the server and sends a fully-rendered document to the browser. SSR improves first content paint and supports SEO for dynamic content. The trade-off is per-request server overhead and the complexity of hydration — the process of attaching browser-side JavaScript behavior to server-rendered HTML.

Static Site Generation (SSG) generates HTML at build time. It is the fastest delivery mechanism and requires no server at runtime. SSG is appropriate for content that changes infrequently and does not vary per user. The trade-off is that stale content requires a rebuild.

Incremental Static Regeneration (ISR) extends SSG with a revalidation window. Pages are served from static HTML but regenerated in the background after a configurable interval. ISR is appropriate for content that changes occasionally but does not need real-time accuracy.

Streaming and partial rendering allows the server to send HTML incrementally, enabling the browser to paint above-the-fold content before the full page is ready. React's Suspense boundaries and Next.js's streaming support make this possible at the component level. Streaming reduces time-to-first-byte for pages with slow data dependencies.

Hydration is a cost, not a given

One of the most underrated performance decisions is hydration strategy. Every server-rendered page that ships JavaScript to the browser must hydrate — which means deserializing server state and re-attaching event handlers. On pages with heavy component trees, hydration can take hundreds of milliseconds on mid-tier mobile devices and block interactivity.

Progressive hydration (deferring non-critical component hydration), islands architecture (hydrating only interactive regions), and server components (zero hydration cost for server-only components) are architectural responses to hydration overhead. A senior engineer should know when hydration cost justifies these patterns and when it does not.

Rendering Strategy Matrix

Use caseBest rendering approachWhyWatch-outs
Marketing landing pageSSGNo personalization; speed and SEO matter mostRebuild pipeline needed for content updates
Blog or documentationSSG or ISRContent changes rarely; static delivery is fast and cheapISR revalidation lag can surface stale content briefly
Product listing pageSSR or ISRInventory and pricing change; SEO mattersPer-request server cost; cache strategy required
User dashboardCSR with SSR shellPersonalized; authenticated; SEO irrelevantBlank shell state; hydration complexity
Real-time feedCSRFrequent updates require client socket or pollingMeaningful initial load requires a skeleton or placeholder
Admin panelCSRInternal tool; no SEO; heavy interactivityLoad time acceptable if JS bundle is controlled
E-commerce product pageSSR or ISRSEO critical; price and availability may varyPersonalization (cart, recommendations) adds CSR layer
Rendering strategy decision flowDecision tree from content requirements to rendering strategy choice: SSG, ISR, SSR, or CSR.Does content change?NoSSGYesNeeds real-time accuracy?NoISRYesIs SEO required?YesSSRNo (auth)CSR
Rendering Strategy Decision FlowRendering strategy selection follows from content type, data freshness, SEO requirements, and personalization needs — not framework defaults.

Senior interview signal

Strong candidates do not default to one rendering approach. They ask about content type, user authentication, SEO requirements, and data freshness before naming a rendering strategy. They understand hydration cost and when to use Suspense boundaries or server components to reduce it. They can describe what breaks if the wrong strategy is chosen.


3. Performance and Performance Budgets

The junior question vs. the senior question

A junior engineer asks: how do I make this component faster?

A senior engineer asks: what is the performance budget for this route, how is it measured, who is accountable for it, and what engineering decisions defend it?

Performance is architectural because it is shaped by data loading strategy, rendering model, bundle structure, image policy, third-party script governance, and interaction design. It cannot be fixed after the fact with a profiler and a few useMemo calls. By the time the profiler surfaces a problem, the architectural decisions that caused it are already hardened in the codebase.

Core Web Vitals as architecture constraints

Core Web Vitals give senior engineers measurable language for performance budgets. The three primary metrics — Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS) — directly correspond to user-perceived experience and Google's ranking signal.

LCP measures when the largest above-the-fold element becomes visible. It is primarily shaped by image loading, server response time, and render-blocking resources. INP measures the longest interaction delay, primarily shaped by main-thread JavaScript work. CLS measures unexpected layout shifts during page load, primarily shaped by image dimension declarations and web font loading.

Each metric is an architecture signal, not just a profiling target.

Performance Budget Table

MetricTargetWhy it mattersEngineering action
LCPUnder 2.5 seconds (75th percentile)Above the fold experience; SEO ranking signalOptimize hero images (WebP, AVIF, preload), reduce server response time, eliminate render-blocking CSS
INPUnder 200msPerceived responsiveness during interactionBreak up long tasks, defer non-critical JS, avoid synchronous state updates on user gesture
CLSUnder 0.1Prevents layout jumps that misdirect clicksSet image and video dimensions, reserve space for async content, avoid injecting DOM above fold
JavaScript bundle sizeUnder 200kB initial (gzipped)Parse and execute cost on mid-tier mobileRoute-level code splitting, tree-shake, audit third-party imports
Time to InteractiveUnder 3.8 seconds on 4G mobileFully interactive state after navigationReduce main-thread hydration work, progressively enhance
Server response timeUnder 200ms (Time to First Byte)Baseline for all downstream metricsEdge caching, CDN, avoid synchronous server work on critical path

Code splitting is architecture

Route-level code splitting is a standard Next.js behavior. Component-level code splitting with dynamic() and React.lazy() is an architectural decision. The question is not whether to split — it is which boundaries to split at, and what the loading experience looks like at each boundary.

Splitting at product domain boundaries (e.g., a heavy charting library loaded only in the analytics section) reduces initial bundle weight without fragmenting the user experience. Splitting too granularly creates waterfall requests. Splitting too coarsely sends unused code to users who will never trigger that path.

// Route-level split (automatic in Next.js App Router)
// Component-level split: defer heavy libraries to secondary interaction

import dynamic from "next/dynamic";

const ReportChart = dynamic(
  () => import("@/components/analytics/ReportChart"),
  {
    loading: () => <ChartSkeleton />,
    ssr: false, // chart library is client-only
  }
);

// The loading prop prevents blank flash.
// ssr: false avoids server hydration of a client-only chart.

Caching strategy is performance architecture

Caching decisions happen at multiple layers: the browser cache, the CDN edge cache, the server-side rendering cache, and the client-side query cache. Each layer has different freshness guarantees and invalidation complexity.

A senior engineer designs cache strategy per data type. Static assets (fonts, icons, images) can use long-lived immutable caches with content-addressed hashing. API responses for frequently updated data need short TTLs or stale-while-revalidate patterns. Personalized content cannot be edge-cached without user context.

Getting caching wrong in either direction — under-caching (too many origin hits) or over-caching (stale data served to users) — is a production performance or correctness problem.

Senior interview signal

Strong candidates tie performance to user outcomes, not just numbers. They explain what LCP failure looks like in terms of user experience, not just metric thresholds. They design performance budgets before writing a line of code. They describe how code splitting boundaries relate to product usage patterns, not just file sizes.


4. Frontend Scalability

The junior question vs. the senior question

A junior engineer asks: how do I keep this component clean?

A senior engineer asks: how do multiple teams build and deploy independently without stepping on each other, without creating a coordination bottleneck, and without accumulating incompatible implementations of the same patterns?

Frontend scalability is not about writing clean code. It is about designing the structural boundaries that let teams move independently at speed without fragmenting the user experience or the underlying platform.

Micro frontends: promise and complexity

Micro frontends decompose a web application into independently developed, independently deployed UI units, owned by separate teams. The promise is team autonomy, isolated deployments, and technology independence. The complexity is integration cost, shared dependency management, and user experience coherence.

Micro frontends are appropriate when the organizational scaling problem is real: multiple teams with conflicting release schedules, genuinely separate product domains, or technology transition requirements. They are not appropriate as a default architectural choice for small or medium teams who want autonomy without the integration tax.

Micro frontend boundary modelAn application shell at the top orchestrates routing. Three independently deployed micro frontends are loaded below for different product domains.Application Shell — Routing, Auth, Shared Navigation, Design TokensTeam CheckoutCart, Payment, ConfirmationIndependent deployOwn React versionTeam CatalogBrowse, Search, FiltersIndependent deployOwns product data modelTeam AnalyticsReports, DashboardsIndependent deployHeavy chart bundleShared: Design System, Auth SDK, Analytics SDK, API Client
Micro Frontend Boundary ModelMicro frontends divide the application into independently owned UI domains. The shell orchestrates routing and shared infrastructure. Each team deploys independently.

Module Federation trade-offs

Module Federation (Webpack 5, Rspack, Vite) enables runtime sharing of JavaScript modules across separately deployed applications. It is the most common technical mechanism for micro frontend integration today.

The benefits: teams can share design system components, auth utilities, and API clients without a shared build step. Changes to shared modules propagate without requiring consumers to redeploy.

The risks: version mismatches between host and remote can cause runtime failures. The Federation contract is invisible at build time — you cannot detect a breaking API change in a remote until it fails at runtime. Network failures in remote loading can bring down parts of the application that depend on them. Governance of the shared module registry requires discipline that many teams underestimate.

A senior architect designs the Federation boundary conservatively. Shared modules should be stable, versioned, and minimal. The integration contract should be typed and validated. Remote loading failures should have explicit fallback behavior.

Monorepos: coordination by design

A monorepo is not an architecture. It is an organizational tool that shapes how teams coordinate. Monorepos offer shared tooling, unified CI, and cross-team refactoring at the cost of longer build times and stricter change discipline.

The failure mode of a monorepo is when teams treat it as permission to create invisible cross-cutting dependencies. A well-run monorepo requires explicit package boundaries, dependency enforcement (nx, turbo, Nx module boundaries), and ownership rules that prevent one team's change from silently breaking another's build.

Senior interview signal

Strong candidates describe when micro frontends are not the right answer. They discuss the integration tax, the governance problem, and the user experience cost of independently deployed UIs that must still feel like one product. They design for autonomy with explicit contracts, not autonomy through isolation.


5. Design Systems

The junior question vs. the senior question

A junior engineer asks: which component from the library should I use?

A senior engineer asks: is this component API designed for the range of use cases it will face, is it accessible by default, does it compose cleanly with the token system, and is it maintainable by teams who did not build it?

Design systems are not UI kits. They are governance models. They encode decisions about color, typography, spacing, interaction, accessibility, and composition into a shared layer that product teams adopt instead of reinventing.

Design tokens are the architecture layer

Design tokens — named values for color, spacing, typography, and motion — are the contract between design intent and implementation. A token like --color-action-primary expresses meaning (the primary action color) rather than value (a hex code). When the hex code changes, the meaning does not.

Tokens enable theming, dark mode, brand customization, and cross-platform consistency. Without tokens, design system changes require finding and replacing values across hundreds of files. With tokens, a single source-of-truth change propagates everywhere the token is consumed.

Senior engineers design the token taxonomy, not just the token values. A flat namespace (--primary-500) is less useful than a semantic namespace (--color-text-primary, --color-surface-raised). The semantic layer expresses intent that survives color palette updates.

Accessibility is architectural, not a QA afterthought

A design system's accessibility posture is set in the component layer. If a Dialog component does not manage focus trap, restore focus on close, support Escape, and use role="dialog" with aria-modal, then every team that uses it will ship an inaccessible dialog. If those patterns are not built in, they will not be consistently applied.

Accessibility cannot be delegated to a final review cycle. It must be designed into component API contracts, validated in component tests, and enforced in contribution guidelines. The cost of retrofitting accessibility into a component library that has already been adopted across fifty product surfaces is enormous.

// Component API design: accessible by default without requiring callers
// to know ARIA internals.

type DialogProps = {
  open: boolean;
  onClose: () => void;
  title: string;      // rendered as accessible heading
  children: React.ReactNode;
  // Caller does not pass role, aria-modal, or focus management —
  // these are invariants the component owns.
};

// The component enforces: focus trap, Escape key close, role=dialog,
// aria-labelledby pointing to the title, focus restoration on close.
// None of these are optional props — they are the contract.

Component API design at scale

A design system component's API surface is a public contract. Once teams depend on it, breaking changes require coordinated upgrades across the organization. Designing the API well from the start is worth the investment.

Good component API design means:

  • Props express intent, not implementation. variant="primary" is better than backgroundColor="#007bff".
  • Composition is preferred over prop explosion. A Card component that accepts header, body, and footer slots is more flexible than a Card with thirty boolean props.
  • Sensible defaults exist for the common case. Teams should not need to pass five props to get a standard dialog.
  • Escape hatches exist for advanced cases without polluting the primary API.

Governance and adoption

A design system that teams do not adopt is a library, not a design system. Adoption requires investment in documentation, versioning, migration guides, contribution processes, and office hours. It also requires organizational support: the incentive structure must make it easier to use the shared pattern than to invent a local one.

The failure mode is a design system that is maintained by a platform team but ignored by product teams because adopting it costs more than the local alternative. Senior engineers and design system leads prevent this by reducing adoption friction and by participating in the products they support, not just delivering components from a distance.

Senior interview signal

Strong candidates describe the governance model, not just the component library. They explain how contribution decisions are made, how breaking changes are managed, how adoption is measured, and how accessibility is validated at scale. They treat design systems as a product with stakeholders, not as a box of shared components.


Holding All Five in Tension

These five dimensions are not independent. Every architectural decision in one dimension creates constraints and implications in the others.

A rendering strategy choice (SSR) affects state architecture (server state must be serialized to the client). A state normalization choice affects performance (un-normalized data creates expensive list operations). A micro frontend boundary decision affects design system governance (which team owns the shared token layer?). An accessibility posture in the design system affects rendering strategy (server-rendered ARIA is more reliable than client-injected ARIA).

Senior frontend engineers are not specialists in one of these five areas. They are generalists who can reason about all five, make trade-offs between them, and communicate the implications to product, design, and engineering leadership.

The third and final article in this series gives you a practical roadmap for developing these skills: what to learn, how to practice, and how to demonstrate architectural capability in your work and in interviews.

Read the Series

  1. What Senior Frontend Engineers Are Expected to Design ← You are here

The covers each of these dimensions at production depth:

  • — full taxonomy with cache invalidation and workflow state
  • — budgets, hydration cost, and Core Web Vitals in depth
  • — design system governance and token strategy
  • — organizational failure modes

Discussion

Comments

Loading comments…

Leave a Comment

Related Articles

Continue the thread