Frontend System Design

The RADIO Framework for Frontend System Design

5 min read

Frontend system design interviews test a completely different skill set than backend system design. While backend focuses on databases, load balancers, and distributed systems, frontend system design focuses on component architecture, state management, rendering performance, and user experience.

How Frontend Differs from Backend System Design

AspectBackend System DesignFrontend System Design
FocusScalability, storage, throughputResponsiveness, rendering, UX
StateDatabase, cache, message queuesComponent state, URL, local storage
NetworkService-to-service communicationAPI calls, real-time connections
FailureServer crashes, network partitionsOffline mode, slow connections
ScaleMillions of requests/second60fps rendering, bundle size

The RADIO Framework

RADIO gives you a repeatable structure for any frontend system design question. The order matters more than the acronym: each step constrains the next, and candidates who jump ahead end up redesigning live.

RADIO — and the failure mode of skipping each step

R · Requirements

3-5 minutes of clarifying questions, split into functional and non-functional. Skip it and you will design the wrong product confidently — the most common way strong engineers fail this round

A · Architecture

The component tree, drawn. Skip it and every later answer becomes hand-waving, because you have no names to refer to when you say 'this part re-renders'

D · Data Model

Where each piece of state lives: server, client, URL, or form. Skip it and you will contradict yourself when the interviewer asks what happens on refresh

I · Interface

The contracts — between components, and with the server. Skip it and the optimisation discussion has nothing concrete to optimise

O · Optimization

Performance, accessibility, error and empty states. This is where candidates differentiate, and it is also the step that gets cut when the earlier ones ran long

Watch the clock against that last box. A 45-minute round that reaches optimisation with five minutes left has effectively skipped the section you were being differentiated on.

Step 1: Requirements

Spend the first 3-5 minutes asking clarifying questions. Split requirements into functional and non-functional:

Functional requirements (what it does):

  • What are the core user actions?
  • What data is displayed?
  • What interactions are supported?

Non-functional requirements (how well it does it):

  • How many concurrent users?
  • What devices and browsers must be supported?
  • What latency targets? (e.g., search results under 200ms)
  • Offline support needed?
  • Accessibility requirements (WCAG level)?

Step 2: Architecture

Sketch the component tree. Start with the page-level layout, then drill into each section:

<App>
  ├── <Header>
  │    ├── <Logo />
  │    ├── <SearchBar />
  │    └── <UserMenu />
  ├── <Sidebar>
  │    └── <Navigation />
  └── <MainContent>
       ├── <FilterPanel />
       └── <ResultsList>
            └── <ResultCard /> (repeated)

Key decisions to explain:

  • Which components are smart (stateful) vs. dumb (presentational)?
  • Where do you split the component boundary?
  • Which components are lazy-loaded?

Step 3: Data Model

Define the state shape and where each piece of state lives:

// Server state (fetched, cached via TanStack Query or SWR)
interface ServerState {
  products: Product[];
  userProfile: User;
  notifications: Notification[];
}

// Client state (local to the UI)
interface ClientState {
  searchQuery: string;
  selectedFilters: Filter[];
  isModalOpen: boolean;
  currentPage: number;
}

// URL state (shareable, bookmarkable)
interface URLState {
  category: string;    // /products?category=electronics
  sortBy: string;      // /products?sort=price-asc
  page: number;        // /products?page=3
}

State management decision tree:

State TypeWhere It LivesTool
Server dataCache layerTanStack Query, SWR
Global UI stateExternal storeZustand, Redux Toolkit
Local UI stateComponentuseState, useReducer
URL-dependentURL paramsuseSearchParams, router
Form dataForm libraryReact Hook Form, Formik

Step 4: Interface (API Layer)

Define the contracts between your frontend and the server:

// REST API design
GET    /api/products?q=laptop&category=electronics&page=1&limit=20
POST   /api/products          // create
PATCH  /api/products/:id      // partial update
DELETE /api/products/:id      // delete

// Response shape
interface APIResponse<T> {
  data: T;
  pagination: {
    page: number;
    totalPages: number;
    totalItems: number;
  };
  error?: { code: string; message: string };
}

REST vs. GraphQL decision:

FactorRESTGraphQL
Multiple resourcesMultiple round tripsSingle query
OverfetchingReturns full objectsRequest exact fields
CachingHTTP cache-friendlyNeeds normalized cache
Team setupSimpler to startNeeds schema + tooling

Real-time updates strategy:

MethodUse WhenOverhead
PollingLow-frequency updates (every 30s+)Low complexity
SSEServer pushes events one-way (notifications, feeds)Medium
WebSocketBidirectional real-time (chat, collaboration)Highest

Step 5: Optimization

This is where you differentiate yourself. Cover:

Rendering performance:

  • Virtualized lists for long scrolling content (react-window, TanStack Virtual)
  • Code splitting with React.lazy() and route-based chunking
  • Optimistic updates for perceived speed

Network performance:

  • Request deduplication and caching
  • Prefetching on hover or route proximity
  • Image optimization (lazy loading, srcset, WebP/AVIF)

Accessibility:

  • Keyboard navigation for all interactive elements
  • ARIA attributes for custom widgets
  • Focus management on route changes

Edge cases:

  • Offline mode with service worker caching
  • Error boundaries for graceful failure
  • Empty states, loading states, error states

Practical Example: RADIO Applied to a Search Page

Let us walk through RADIO for a product search page:

R (Requirements): Users search products by keyword, filter by category and price, sort results, paginate through them. Must work on mobile. Target: results in under 200ms.

A (Architecture):

<SearchPage>
  ├── <SearchBar />          // debounced input
  ├── <ActiveFilters />      // chips showing current filters
  ├── <FilterSidebar />      // category, price range
  └── <ResultsPanel>
       ├── <SortControls />  // relevance, price, rating
       ├── <ProductGrid>
       │    └── <ProductCard /> (repeated)
       └── <Pagination />

D (Data Model): Search query and filters live in URL params (shareable). Products are server state cached by TanStack Query with the query string as cache key.

I (Interface): GET /api/search?q=laptop&category=electronics&minPrice=500&sort=price-asc&page=1 returns paginated results with facet counts.

O (Optimization): Debounce search input by 300ms. Prefetch next page. Virtualize product grid on mobile. Use <img loading="lazy">. Add aria-live="polite" region for screen reader announcements when results update.

Interview tip: Always draw the component tree and state flow on the whiteboard. Interviewers want to see your thought process, not just hear it.

Next, we will walk through three classic frontend system design problems step by step. :::

Quiz

Module 4: Frontend System Design

Take Quiz
Was this lesson helpful?

Sign in to rate