The RADIO Framework
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.
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.
| Aspect | Backend System Design | Frontend System Design |
|---|---|---|
| Primary concern | Scalability, data consistency, throughput | User experience, rendering, interactivity |
| Diagrams show | Servers, databases, queues, load balancers | Component trees, data flow, state architecture |
| Performance means | Low latency, high QPS, efficient queries | Fast paint, smooth interactions, small bundles |
| State lives in | Databases, caches, message queues | Component state, stores, URL, browser storage |
| API design focus | REST/GraphQL endpoints, pagination, auth | Client-server contract AND component APIs (props/hooks) |
| Non-functional | Availability, partition tolerance, consistency | Accessibility, i18n, offline support, SEO |
| Testing focus | Load testing, integration, chaos engineering | Visual regression, interaction testing, a11y audits |
| Time allocation | ~60 min: heavy on infrastructure | ~40 min: balanced across all RADIO phases |
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.
- 1Always separate functional from non-functional requirements — they drive different decisions
- 2Explicitly state scope boundaries — what you will and won't cover in the interview
- 3Ask about scale early — 100 items vs 1M items leads to completely different architectures
- 4Non-functional requirements (a11y, i18n, offline) differentiate senior from mid-level answers
- 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
- Major UI regions: Header, Sidebar, Main Content, Footer
- Page-level components: Feed, Profile, Settings, Detail View
- Shared components: Modal, Toast, Navigation
- Data flow direction: Top-down props? Context? Global store?
- 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?
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 Type | Where It Lives | Examples |
|---|---|---|
| Server state | Fetched from API, cached client-side | Posts, user profiles, comments |
| Client state | Component or store, never persisted | Modal open/closed, active tab, form inputs |
| URL state | Query params, path segments | Current page, filters, sort order, search query |
| Browser state | localStorage, sessionStorage, IndexedDB | Theme preference, draft posts, offline cache |
| Derived state | Computed from other state, never stored | Filtered 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.
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:
- Client-server APIs — How the frontend talks to the backend
- 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.
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-windowor native CSScontent-visibility: auto. - Image optimization: Responsive images with
srcset, WebP/AVIF formats, lazy loading withloading="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
dangerouslySetInnerHTMLwith 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.
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:
| Phase | Time | What to Deliver |
|---|---|---|
| Requirements | 4-5 min | Written list of functional + non-functional requirements, scope boundaries |
| Architecture | 7-8 min | Component tree diagram, route structure, key architectural decisions |
| Data Model | 7-8 min | Entity definitions, state taxonomy, normalization decision |
| Interface | 7-8 min | Client-server API shapes, component API signatures, data flow |
| Optimizations | 8-10 min | 2-3 deep dives into performance, a11y, or other relevant areas |
| Buffer | 2-3 min | Interviewer questions, pivots, areas they want to explore deeper |
The Flow
-
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..."
-
Write requirements visibly. Use the whiteboard or shared doc. The interviewer should see your structured thinking.
-
Draw before you speak. A component tree diagram communicates more than 5 minutes of verbal explanation.
-
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."
-
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 do | What 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 |
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.
- 1RADIO: Requirements → Architecture → Data Model → Interface → Optimizations — always in this order
- 2Spend the first 5 minutes on requirements — it saves you from designing the wrong system
- 3Architecture means component trees and data flow, not servers and databases
- 4State taxonomy matters: server state, client state, URL state, browser state, derived state
- 5Component APIs are interfaces too — design them with the same rigor as network APIs
- 6Go deep on 2-3 optimizations rather than listing 15 buzzwords
- 7Always mention accessibility — it separates senior from mid-level in the interviewer's mind