Skip to content

The RADIO Framework

advanced18 min read

Frontend System Design Is a Different Beast

You walk into a system design interview. The interviewer says: "Design a news feed." Your backend instincts kick in — databases, load balancers, message queues, sharding strategies. You start drawing servers.

Stop.

If this is a frontend system design interview, you just burned 10 minutes on the wrong thing.

Frontend system design isn't about infrastructure. It's about what happens after the data arrives at the browser. Component architecture. State management. Rendering strategies. API contracts between client and server. Performance that users can feel. Accessibility that works for everyone.

The interviewer doesn't care about your database schema. They care about how you'd structure the component tree, where state lives, how you handle optimistic updates, what happens offline, and whether you've thought about accessibility.

Mental Model

Think of backend system design as designing a factory — raw materials come in, products go out. You think about supply chains, warehouse capacity, and throughput. Frontend system design is designing a storefront. The factory already exists. Your job is to create the experience customers interact with — the layout, the flow, the speed, and making sure every customer can use it, including those with disabilities. Both require engineering. They just solve fundamentally different problems.

AspectBackend System DesignFrontend System Design
Primary concernScalability, data consistency, throughputUser experience, rendering, interactivity
Diagrams showServers, databases, queues, load balancersComponent trees, data flow, state architecture
Performance meansLow latency, high QPS, efficient queriesFast paint, smooth interactions, small bundles
State lives inDatabases, caches, message queuesComponent state, stores, URL, browser storage
API design focusREST/GraphQL endpoints, pagination, authClient-server contract AND component APIs (props/hooks)
Non-functionalAvailability, partition tolerance, consistencyAccessibility, i18n, offline support, SEO
Testing focusLoad testing, integration, chaos engineeringVisual regression, interaction testing, a11y audits
Time allocation~60 min: heavy on infrastructure~40 min: balanced across all RADIO phases
Quiz
Your interviewer asks you to design a collaborative document editor. What should you focus on first in a frontend system design interview?

Enter the RADIO Framework

RADIO is a structured approach to frontend system design created by GreatFrontEnd. It stands for:

  • R — Requirements
  • A — Architecture
  • D — Data Model
  • I — Interface Definition
  • O — Optimizations

It gives you a repeatable process so you don't freeze, ramble, or skip critical areas. Every FAANG frontend interview can be navigated with this framework.

R — Requirements: The 5 Minutes That Save You 30

The biggest mistake candidates make? They start drawing components before they understand the problem.

Requirements gathering is not a formality. It's where you demonstrate product thinking and prevent yourself from solving the wrong problem.

Functional Requirements

These describe what the system does. Ask yourself:

  • What are the core user actions? (create, read, update, delete, search, filter, sort)
  • What content types exist? (text, images, video, rich text, code)
  • What are the user roles? (viewer, editor, admin)
  • What's the interaction model? (click, drag, keyboard, touch, voice)
  • Is there real-time behavior? (live updates, collaboration, notifications)

Non-Functional Requirements

These are the constraints that shape your architecture. This is where senior candidates shine:

  • Performance: What are the target load times? How many items render on screen? Is there infinite scroll?
  • Accessibility: WCAG compliance level? Screen reader support? Keyboard navigation?
  • Offline support: Should the app work without a network? What degrades gracefully?
  • Internationalization: RTL languages? Date/number formatting? Translation workflow?
  • SEO: Is this content indexable? Do we need SSR/SSG?
  • Browser support: Modern only? IE11? Mobile browsers?

Scope Boundaries

This is critical. You have 35-45 minutes. You cannot design everything. Explicitly state:

  • "I'll focus on the feed view and post creation, but skip the notification system for now."
  • "I'll assume authentication is handled — I won't design the login flow."
  • "I'll design for web only, not native mobile."

The interviewer respects candidates who proactively scope. It shows you can prioritize under constraints — exactly what you do in production.

Quiz
During the requirements phase, your interviewer says 'Design an image gallery like Google Photos.' Which clarifying question provides the MOST architectural value?
Key Rules
  1. 1Always separate functional from non-functional requirements — they drive different decisions
  2. 2Explicitly state scope boundaries — what you will and won't cover in the interview
  3. 3Ask about scale early — 100 items vs 1M items leads to completely different architectures
  4. 4Non-functional requirements (a11y, i18n, offline) differentiate senior from mid-level answers
  5. 5Write requirements down visibly — the interviewer should see your structured thinking

A — Architecture: The Component Blueprint

With requirements clear, you draw the high-level architecture. This is not a wireframe. This is not a backend system diagram. It's a component tree showing major UI regions and how they relate.

What to Include

  1. Major UI regions: Header, Sidebar, Main Content, Footer
  2. Page-level components: Feed, Profile, Settings, Detail View
  3. Shared components: Modal, Toast, Navigation
  4. Data flow direction: Top-down props? Context? Global store?
  5. Client-side routing: Which components map to which routes?

Example: News Feed Architecture

App
├── Layout
│   ├── Header (search, nav, user menu)
│   ├── Sidebar (filters, trending, suggestions)
│   └── MainContent
│       ├── CreatePost (rich text editor, media upload)
│       ├── FeedList (infinite scroll container)
│       │   ├── FeedItem (post content, media, reactions)
│       │   │   ├── PostHeader (author, timestamp, menu)
│       │   │   ├── PostBody (text, images, video, link preview)
│       │   │   ├── ReactionBar (like, comment, share counts)
│       │   │   └── CommentSection (collapsed by default)
│       │   └── FeedItem ...
│       └── LoadingSkeletons
└── Overlays
    ├── MediaViewer (lightbox for images/video)
    └── ShareSheet (share options)

Key Architecture Decisions to Call Out

  • Server Components vs Client Components: Which parts need interactivity? A feed item's static content can be a Server Component. The reaction bar needs client-side state.
  • Route structure: /feed, /feed/:postId (parallel route for modal?), /profile/:userId
  • Code splitting boundaries: Each route is automatically split. Heavy components (rich text editor, media viewer) get dynamic imports.
  • Error boundaries: Where do you catch errors? Per-feed-item (so one broken post doesn't crash the feed) or per-section?
Keep it high-level

A common mistake is drawing every single component. The interviewer wants to see your ability to identify the right abstractions, not your ability to list 50 components. Focus on the components that carry architectural weight — the ones where data flows through, state lives, or significant rendering decisions happen.

D — Data Model: Where State Lives

This is where strong candidates separate themselves. The data model defines what data exists and where it lives.

The State Taxonomy

Not all state is equal. You need to know where each piece belongs:

State TypeWhere It LivesExamples
Server stateFetched from API, cached client-sidePosts, user profiles, comments
Client stateComponent or store, never persistedModal open/closed, active tab, form inputs
URL stateQuery params, path segmentsCurrent page, filters, sort order, search query
Browser statelocalStorage, sessionStorage, IndexedDBTheme preference, draft posts, offline cache
Derived stateComputed from other state, never storedFiltered list, character count, validation status

Normalized vs Denormalized

For a news feed, posts contain authors. Should you store author data inside each post (denormalized) or separately (normalized)?

// Denormalized — simple but duplicated
interface Post {
  id: string
  content: string
  author: {
    id: string
    name: string
    avatar: string
  }
  comments: Comment[]
}

// Normalized — no duplication, relationships via IDs
interface Store {
  posts: Record<string, Post>
  users: Record<string, User>
  comments: Record<string, Comment>
}

interface Post {
  id: string
  content: string
  authorId: string
  commentIds: string[]
}

When to normalize: When entities are shared across views (a user appears in posts, comments, and suggestions), when updates need to propagate everywhere (user changes avatar), when the dataset is large.

When to denormalize: When data is read-heavy and write-rare, when entities aren't shared, when simplicity matters more than consistency.

Quiz
You're designing a chat application. Messages have senders, and users can appear in multiple conversations. A user updates their display name. With a denormalized data model, what problem occurs?

Entity Relationships

Draw out the entities and their relationships. For a news feed:

User ──1:N──→ Post
User ──1:N──→ Comment
Post ──1:N──→ Comment
Post ──N:M──→ User (reactions)
User ──N:M──→ User (follow)

This isn't just documentation. It drives your API design, your cache invalidation strategy, and your component prop types.

I — Interface Definition: The Contracts

Interface means two things in frontend system design:

  1. Client-server APIs — How the frontend talks to the backend
  2. Component APIs — How components talk to each other (props, hooks, events)

Client-Server APIs

Define the key endpoints your frontend needs. Be specific about request/response shapes:

// GET /api/feed?cursor=abc&limit=20
interface FeedResponse {
  posts: Post[]
  nextCursor: string | null
  hasMore: boolean
}

// POST /api/posts
interface CreatePostRequest {
  content: string
  mediaIds: string[]
  visibility: "public" | "friends" | "private"
}

// WebSocket: ws://api/feed/realtime
interface FeedUpdate {
  type: "new_post" | "reaction" | "comment" | "delete"
  payload: Post | Reaction | Comment | string
}

Call out important design decisions:

  • Cursor-based vs offset pagination: Cursor-based is better for feeds where items are inserted/deleted frequently. Offset breaks when new items push everything down.
  • REST vs GraphQL: GraphQL shines when the frontend needs flexible queries (different views need different fields from the same entity). REST is simpler when views map 1:1 to resources.
  • WebSocket vs SSE vs polling: Real-time feeds need push. WebSocket is bidirectional (needed for chat). SSE is simpler for one-way streams. Polling is the fallback.

Component APIs (Props and Hooks)

This is what makes frontend system design unique. Your components are APIs too:

interface FeedItemProps {
  post: Post
  onReact: (postId: string, reaction: ReactionType) => void
  onComment: (postId: string) => void
  onShare: (postId: string) => void
  isCompact?: boolean
  showComments?: boolean
}

// Custom hook — encapsulates feed data fetching logic
function useFeed(options: {
  filter?: FeedFilter
  limit?: number
}): {
  posts: Post[]
  isLoading: boolean
  error: Error | null
  loadMore: () => void
  hasMore: boolean
}

Good component APIs follow the same principles as good library APIs: minimal surface area, sensible defaults, escape hatches for edge cases.

Common Trap

Don't design component APIs in isolation. A common trap is designing the perfect FeedItem component that needs 15 props because it handles every variation. Instead, use composition: FeedItem renders PostBody, which can be a TextPost, ImagePost, or VideoPost. The variation lives in the children, not in a sea of boolean props.

O — Optimizations: Where Seniors Shine

This is your chance to demonstrate depth. Don't list 20 buzzwords. Pick 2-3 areas and go deep.

Performance

  • Virtualized lists: For feeds with hundreds of items, only render what's in the viewport. Libraries like react-window or native CSS content-visibility: auto.
  • Image optimization: Responsive images with srcset, WebP/AVIF formats, lazy loading with loading="lazy", blur-up placeholders.
  • Code splitting: Route-level is automatic in Next.js. Component-level via dynamic() for heavy widgets (rich text editor, chart library).
  • Optimistic updates: When a user likes a post, update the UI instantly before the server responds. Roll back on failure.
  • Skeleton screens: Show content-shaped placeholders during loading instead of spinners. Reduces perceived load time.

Accessibility

  • Keyboard navigation: Every interactive element reachable via Tab. Feed items navigable with arrow keys. Escape closes modals.
  • Screen reader announcements: Live regions for new posts (aria-live="polite"). Meaningful alt text for images.
  • Focus management: When opening a modal, trap focus inside. When closing, return focus to the trigger.
  • Reduced motion: Respect prefers-reduced-motion. Disable parallax, auto-playing video, and complex transitions.

Security

  • XSS prevention: Sanitize user-generated content before rendering. Never use dangerouslySetInnerHTML with untrusted input.
  • CSRF: Use tokens for state-changing requests.
  • Content Security Policy: Restrict what scripts, styles, and images can load.
  • Auth tokens: Store in httpOnly cookies, not localStorage (XSS can steal localStorage).

Other Optimizations

  • Offline support: Service workers + IndexedDB for cached content. Queue mutations and sync when back online.
  • Internationalization: RTL layout support, locale-aware date/number formatting, string externalization.
  • SEO: Server-side rendering for public content. Proper meta tags, Open Graph, structured data.
Optimistic updates — the pattern that makes apps feel instant

Here's the pattern most candidates mention but few implement correctly:

async function handleLike(postId: string) {
  // 1. Optimistically update UI
  updatePostInCache(postId, (post) => ({
    ...post,
    isLiked: true,
    likeCount: post.likeCount + 1,
  }))

  try {
    // 2. Send request to server
    await api.likePost(postId)
  } catch (error) {
    // 3. Roll back on failure
    updatePostInCache(postId, (post) => ({
      ...post,
      isLiked: false,
      likeCount: post.likeCount - 1,
    }))
    showToast("Couldn't like this post. Try again.")
  }
}

The key insight: the rollback must restore the exact previous state, not just decrement. If two optimistic updates overlap (user likes then unlikes quickly), you need a proper state queue — not just increment/decrement counters.

Quiz
You're optimizing a social media feed that renders 500+ posts. Which optimization has the BIGGEST impact on initial render performance?

Structuring Your 35-45 Minute Answer

Time management separates candidates who get offers from those who get "good but not quite." Here's a battle-tested allocation:

PhaseTimeWhat to Deliver
Requirements4-5 minWritten list of functional + non-functional requirements, scope boundaries
Architecture7-8 minComponent tree diagram, route structure, key architectural decisions
Data Model7-8 minEntity definitions, state taxonomy, normalization decision
Interface7-8 minClient-server API shapes, component API signatures, data flow
Optimizations8-10 min2-3 deep dives into performance, a11y, or other relevant areas
Buffer2-3 minInterviewer questions, pivots, areas they want to explore deeper

The Flow

  1. Start by restating the problem in your own words. "So we're designing a news feed similar to Facebook/LinkedIn — a scrollable list of posts with text, images, reactions, and comments. Let me clarify a few things before I start..."

  2. Write requirements visibly. Use the whiteboard or shared doc. The interviewer should see your structured thinking.

  3. Draw before you speak. A component tree diagram communicates more than 5 minutes of verbal explanation.

  4. Narrate your trade-offs. Don't just say "I'd use cursor pagination." Say "I'd use cursor-based pagination because items can be inserted or deleted from the feed in real time — offset-based would cause duplicates or missed items when the list shifts."

  5. Check in with the interviewer. After requirements and after architecture, ask: "Does this scope look right? Would you like me to go deeper on any area before I move on?"

Common Anti-Patterns

What developers doWhat they should do
Jumping straight to code or component names without gathering requirements
Without requirements, you'll design for the wrong problem. You might build an offline-first architecture when the interviewer wanted a real-time collaborative system.
Spend 4-5 minutes clarifying requirements, scope, and constraints before touching architecture
Drawing a backend system diagram with databases, load balancers, and message queues
Frontend system design interviews evaluate your frontend expertise. The interviewer assumes the backend exists. They want to see how you structure the client-side system.
Draw a component tree with data flow, state locations, and client-side architecture
Listing 15 optimizations as bullet points without depth on any
Breadth without depth signals surface-level knowledge. Anyone can say 'use lazy loading.' The interviewer wants to hear how you'd implement it, what the fallback is, and how it affects the loading experience.
Pick 2-3 optimizations most relevant to the problem and explain the implementation, trade-offs, and impact
Designing components with 20 boolean props to handle every variation
A component with isCompact, isExpanded, showMedia, showComments, showReactions, isHighlighted, etc. is untestable and unmaintainable. Composition keeps each component focused and testable.
Use composition — render different child components instead of configuring one mega-component
Ignoring non-functional requirements like accessibility, i18n, and offline support
Real production systems serve diverse users on varying networks. An interviewer who hears 'and we'd add aria-live regions for new posts so screen reader users know content updated' knows you build for the real world.
Proactively mention and design for non-functional requirements — they differentiate senior from mid-level
Quiz
You're 20 minutes into a 40-minute frontend system design interview. You've covered requirements and architecture thoroughly but haven't started the data model. What should you do?

Putting It All Together

The RADIO framework isn't just for interviews. It's how senior engineers actually think about frontend systems in production. When your tech lead asks you to design a new feature, you instinctively think:

  • What are the requirements and constraints?
  • How does this fit into the existing component architecture?
  • Where does the new state live?
  • What APIs do we need — both network and component-level?
  • What are the performance and accessibility implications?

That's RADIO. It's not a hack for interviews — it's structured thinking for frontend architecture.

Key Rules
  1. 1RADIO: Requirements → Architecture → Data Model → Interface → Optimizations — always in this order
  2. 2Spend the first 5 minutes on requirements — it saves you from designing the wrong system
  3. 3Architecture means component trees and data flow, not servers and databases
  4. 4State taxonomy matters: server state, client state, URL state, browser state, derived state
  5. 5Component APIs are interfaces too — design them with the same rigor as network APIs
  6. 6Go deep on 2-3 optimizations rather than listing 15 buzzwords
  7. 7Always mention accessibility — it separates senior from mid-level in the interviewer's mind
Quiz
Which phase of the RADIO framework directly determines your caching and cache invalidation strategy?
1/10