When you walk into a frontend system design round, it can feel overwhelming. The scope is often broad, the expectations are high, and interviewers want to see structured thinking rather than just scattered solutions. This is where a framework becomes essential.
One such framework that works brilliantly for frontend design interviews is RADIO. Think of it as a mental checklist that ensures you cover all key aspects: Requirement Analysis, Architecture, Data Model, Interface (APIs), and Optimization.
But let’s not stop at the basics—let’s extend RADIO into a more powerful guide you can rely on in any high-level system design conversation.
R – Requirements
As before, this is your foundation. Without understanding what to build, you’ll end up optimizing the wrong thing. Always start here.
Functional Requirements
Clarify what exactly you’re building:
- What’s the scope? (e.g., Feed, Form, Dashboard)
- What features are expected? (e.g., CRUD, Search, Filtering)
- What user actions should be supported?
- Are there different user roles (e.g., admin, viewer, guest)?
- Any real-time updates or live interactions?
Non-Functional Requirements
These show your foresight:
- i18n / L10n support?
- Offline functionality?
- Accessibility (a11y) compliance?
- Performance expectations (load times, interactions)?
- Security considerations (XSS, CSRF)?
👉 Tip: List constraints upfront — “We’re targeting web only,” or “Mobile-first approach.” It keeps you aligned and time-efficient.
A – Architecture
Before we jump into drawing boxes and arrows, it's worth spending a few minutes aligning with the interviewer on some high-level architectural decisions. These decisions influence the entire design, and making assumptions upfront demonstrates that you're thinking like an architect rather than immediately jumping into implementation.
In most frontend system design interviews, you won't lose points for asking thoughtful questions—in fact, interviewers expect it. Spending 2–5 minutes discussing these trade-offs ensures both of you are designing the same system.
While every problem is unique, the following topics come up in almost every frontend system design interview and are applicable to nearly 90% of scenarios. Use them as conversation starters to establish the architectural direction before moving on to the actual system diagram.
1. SSR vs CSR vs SSG
Clarify how the UI will be delivered to the client.
CSR (Client-Side Rendering) offers lighter servers and highly interactive experiences but requires more client resources and may delay initial render.
SSR (Server-Side Rendering) improves first paint and SEO, ideal for content-heavy or public-facing applications.
SSG (Static Site Generation) pre-renders stable pages at build time, offering excellent performance and scalability where data does not change frequently.
Explain why your chosen approach aligns with the product’s latency, SEO, and interactivity needs.
2. SPA vs MPA
Define whether the solution is a single-page app or a multi-page application.
SPA provides seamless transitions and rich interaction but requires careful handling of routing, hydration, and large bundle sizes.
MPA offers isolated pages, simpler performance characteristics, and clearer security boundaries, but may feel less fluid.
Connect this choice to user expectations, caching strategies, and deployment complexity.
3. REST vs GraphQL
Explain how data enters the application and how it is cached or synchronized.
REST and GraphQL each affect granularity, overfetching, and client-side schema design.
Pagination strategies (cursor-based or offset-based) determine how lists scale.
Caching layers—memory cache, HTTP cache, React Query caches—prevent redundant requests and improve responsiveness.
If mutations are required, clarify whether you use optimistic updates, background refetching, or rollback strategies.
4. Transport Protocol Choice (HTTP/1.1 vs HTTP/2 vs HTTP/3)
Demonstrate awareness of the underlying network protocol and its impact on performance.
HTTP/1.1 handles only one request per connection, leading to head-of-line blocking on resource-heavy pages.
HTTP/2 adds multiplexing, stream prioritisation, and header compression, significantly improving concurrency and API performance.
HTTP/3, based on QUIC, removes transport-level head-of-line blocking and offers faster, more resilient connections—especially valuable for realtime features and mobile networks.
Connecting protocol capabilities to rendering and data-fetching decisions shows senior-level architectural awareness.
5. Communication protocols (WebSocket, SSE, Long Polling, Short Polling)
Specify how the application receives live data when required.
WebSockets support bi-directional communication for highly interactive features.
Server-Sent Events offer a lightweight one-way stream ideal for dashboards or notifications.
Long polling keeps a request open until new data arrives, suitable when persistent sockets aren't permitted.
Short polling periodically triggers requests and works for low-frequency or non-critical updates.
Highlight how the UI integrates updates into existing state while maintaining consistency and minimizing rerenders.
Architecture diagram
Here is the place where we demonstrate our architecture diagram and overall communication. Dedicate about 10-15 minutes here. Building a perfect architecture in this limited time is challenging, so it helps to have a strong mental model ready.
One highly recommended approach is to think in terms of an MVC-like architecture. In fact, for nearly 90% of design questions, this structure will work:
- View: Defines what all views are needed in the frontend. For example, in a Facebook feed, we might need
Post View,Post List,Comments,Likesetc. - Controller: Manages the data, formatting it according to frontend requirements, and handling operations before rendering. For example:
PostCreation Controller,Comments Controller,Likes Controller. - Services: Deals with API calls, caching, and service workers. For example:
Fetch Posts,Fetch Comments,Fetch Likes. Services handle client-server communication and caching policies.
👉 Your duty as the interviewee is to clearly state what each unit is responsible for and how they intract each other.
When Should You Draw a Full Architecture Diagram?
Not every interview problem requires a large architecture diagram.
A full architecture diagram is most valuable when the application has multiple modules, complex data flows, or several interacting features.
Examples:
- Facebook Feed
- Google Docs
- Slack
- Trello
- YouTube
- E-commerce applications (Amazon, Flipkart)
These systems typically involve multiple pages, shared state, background synchronization, caching, authentication, and communication between many independent modules. A high-level architecture diagram helps the interviewer understand how everything fits together.
When Is a Component Tree a Better Choice?
For smaller or self-contained problems, drawing a detailed architecture diagram often adds unnecessary complexity. Instead, use a component tree to show the UI hierarchy and explain where state, props, and responsibilities live.
A component tree is usually sufficient for interview questions such as:
- Autocomplete / Typeahead
- File Explorer
- Star Rating
- Kanban Board
- Modal Manager
- Infinite Scroll List
- Todo Application
- Shopping Cart
- Calendar
- Image Gallery
In these cases, the interview is more focused on component composition, state management, rendering strategy, and communication between components than on large-scale system architecture.
Rule of thumb: If you're designing an entire product with multiple features and services, start with a high-level architecture diagram. If you're designing a single feature or UI module, a component tree is often clearer, faster to draw, and easier for the interviewer to follow.
D – Data Model
The Data Model section explains how data flows through the frontend application after it is received from the backend. Here, we discuss what data the UI stores, where it is stored, and how different types of state are managed to keep the application scalable and maintainable.
A useful way to think about this is:
What data exists in the application, and where should each piece of data live?
1. Identify the Different Types of State
The first step is identifying every type of state required by the application.
For almost every frontend system, state can be divided into three categories:
- Local State
- Global State
- Server State
2. Local State (Component State)
Local state belongs to a single component and isn't shared with the rest of the application.
Typical examples include:
- Input field values
- Modal visibility
- Dropdown open/close state
- Selected tab
- Form validation errors
- Loading spinner for a button
- Hover/expanded state
CommentBox
├── commentText
├── isSubmitting
└── validationError
PostCard
├── showComments
├── isExpanded
└── isMenuOpenThese states usually live inside the component using useState.
3. Global State (Application State)
Global state contains information that is shared across multiple components or pages.
Typical examples:
- Logged-in user
- Authentication token
- Theme (Dark / Light)
- Language
- Feature flags
- Selected filters
- Notification count
Global Store
├── currentUser
├── theme
├── language
├── notificationCount
└── appliedFiltersThis state can be managed using:
- Redux
- Zustand
- Context API
- Jotai
The key principle is:
Store only data that multiple parts of the application need.
4. Server State (Backend Data)
Server state is data fetched from the backend.
Examples include:
- Posts
- Products
- Comments
- User profiles
- Search results
- Notifications
- Orders
Unlike local or global state, server state needs to handle:
- Fetching
- Caching
- Pagination
- Background refresh
- Retries
- Optimistic updates
- Synchronization with the backend
Modern applications typically use libraries such as:
- React Query (TanStack Query)
- SWR
- RTK Query
Rather than storing backend entities in Redux, these libraries manage server data more efficiently.
5. Organizing Server Data
Once server data is fetched, decide how it should be organized.
For small applications, storing arrays is sufficient.
posts = [
{ id: 1, ... },
{ id: 2, ... }
]For larger applications where the same entity appears in multiple places, normalization avoids duplication.
posts
byId
├── p1
├── p2
└── p3
allIds
├── p1
├── p2
└── p3
Example Data Model (Simplified Post System)
{
"userData": {
"id": "user_123",
"name": "Alice",
"email": "[email protected]"
},
"posts": "Array<Post>",
"Post": {
"id": "post_456",
"authorId": "user_123",
"content": "This is my first post!",
"createdAt": "2025-10-04T10:00:00Z"
}
}
👉 In this normalized structure:
userDatastores user info once and is referenced viaauthorId.postsis an array of Post objects.
This ensures easier updates (if the user changes their name, you only update it in one place).
Benefits include:
- Faster updates
- Constant-time lookup
- Single source of truth
- No duplicate copies of the same entity
6. Derived State
Not everything should be stored.
Only store the source of truth and derive the rest when needed.
Examples:
Instead of storing:
isAuthorderive it from:
currentUser.id === post.authorIdOther examples:
- Sorted lists
- Filtered lists
- Total counts
- Visibility flags
Derived state reduces duplication and keeps data consistent.
7. Handling Data Updates
Whenever the user performs an action, the UI should update efficiently.
A typical flow is:
User clicks Like
│
▼
Optimistically update UI
│
▼
Send API request
│
▼
Server Response
├── Success
│ ▼
│ Update cache
│
└── Failure
▼
Rollback UIModern server-state libraries handle this workflow with minimal boilerplate.
I – Interface (Frontend ↔ Backend Contract)
The Interface section defines how the frontend communicates with external systems, primarily the backend APIs. A well-designed API contract enables efficient rendering, caching, pagination, optimistic updates, and future extensibility.
When discussing the interface, focus on answering these questions:
- What APIs does the frontend need?
- What should each endpoint return?
- How should pagination work?
- How are mutations handled?
- How are errors communicated?
- Is the API easy to extend in the future?
1. API Endpoint Design
A resource-oriented API keeps the contract predictable and scalable.
Instead of action-based endpoints like:
/createPost
/getAllPosts
/updateLikeprefer REST-style resources:
Method | Endpoint | Purpose |
|---|---|---|
GET |
| Fetch feed |
GET |
| Fetch single post |
POST |
| Create post |
PATCH |
| Edit post |
DELETE |
| Delete post |
POST |
| Like / Unlike |
GET |
| Fetch comments |
POST |
| Add comment |
This keeps APIs intuitive and easy to discover.
2. URL Design & Naming Conventions
The API should follow consistent naming conventions.
Use nouns instead of verbs
/posts
/comments
/users
/productsinstead of
/getPosts
/createComment
/updateUserUse nested resources only for ownership
/posts/:id/comments
/users/:id/ordersThis clearly represents relationships.
Use query parameters for filtering
GET /posts
?page=2
?limit=20
?cursor=abc123
?sort=latest
?category=frontend
?search=reactThis makes APIs flexible without creating many endpoints.
3. Response Structure
The API should return everything required for rendering the UI.
Instead of forcing multiple API calls:
{
"id": "p1",
"content": "...",
"likes": 15
}return UI-friendly data:
{
"id": "p1",
"content": "Hello World",
"author": {
"id": "u1",
"name": "John",
"avatar": "..."
},
"likes": 15,
"likedByUser": true,
"commentCount": 8,
"createdAt": "...",
"updatedAt": "..."
}Fields like:
likedByUsercommentCountisFollowingcanEdit
allow the frontend to render immediately without extra computation or additional network requests.
4. Pagination Strategy
For large datasets, pagination is essential.
Offset Pagination
GET /posts?page=3&limit=20Easy to implement but problematic for dynamic feeds because newly inserted items can shift page boundaries.
Cursor Pagination
GET /posts?cursor=abc123&limit=20Example response:
{
"data": [...],
"nextCursor": "xyz789",
"hasMore": true
}Advantages:
- Better for infinite scrolling
- Handles live inserts gracefully
- Efficient for large datasets
- Works naturally with React Query's infinite queries
5. Mutation Contract
Mutations should return the updated resource instead of only a success status.
Instead of:
{
"success": true
}prefer:
{
"id": "p1",
"likes": 43,
"likedByUser": true
}Returning the updated entity allows the frontend to update its cache immediately without making another GET request.
Examples:
POST /posts/:id/like
POST /posts/:id/comments
PATCH /posts/:id
DELETE /posts/:idThis integrates well with optimistic UI updates and client-side caching libraries like React Query.
6. Error Handling
Errors should follow a consistent structure so the frontend can display meaningful messages and implement retry logic.
Example:
{
"error": {
"code": "POST_NOT_FOUND",
"message": "Post does not exist"
}
}Using structured error codes enables the frontend to:
- Show user-friendly messages
- Retry transient failures
- Handle authentication errors
- Trigger specific UI flows (e.g., redirect to login)
7. API Versioning
As the application evolves, APIs should remain backward compatible.
Common strategies include:
/api/v1/posts
/api/v2/postsor versioning through headers.
Versioning allows new features to be introduced without breaking existing clients.
O – Optimization (Performance, Accessibility, Security)
Performance Optimization
Optimization is where you shine as a frontend engineer. Interviewers love when you dive deep here. While this topic can easily take more than an hour to fully discuss, in an interview you will rarely get more than 10 minutes. Hence, focus on major categories.
1. Network & Performance
- Gzip / Brotli compression
- HTTP/2 for multiplexing requests
- Bundle splitting and code splitting
- Prefetch / preload critical resources
- Lazy loading of components and images
- Image optimization (webp, responsive sizes)
- Efficient caching strategies (Cache-Control, Service Workers)
- CDN usage for static assets
2. Rendering Optimization
- Inline critical CSS
- Non-critical CSS in deferred mode
- Efficient CSS class naming to reduce redundancy
- Virtualization for long lists
- Skeleton loading for better perceived performance
- Minimizing reflows and repaints
- Memoization of components where applicable
- Avoiding unnecessary DOM updates
3. JavaScript Performance
- Using Web Workers for heavy computations
- Minified and tree-shaken JS code
- Caching computations and results
- Debouncing/throttling expensive operations
- Optimizing event listeners
4. Additional Optimizations
- Accessibility optimizations for faster rendering (a11y best practices)
- Progressive enhancement for slower networks
- Monitoring and profiling with Lighthouse, Web Vitals, or similar tools
Accessibility
- Semantic HTML (e.g.,
<button>, not<div>) - ARIA roles for dynamic/custom elements
- Keyboard navigation and focus states
- Color contrast checks
Security
- Escape all dynamic HTML (XSS prevention)
- Avoid
dangerouslySetInnerHTML - Secure tokens in memory (not
localStorage) - CSRF-aware form submissions
Testing (Optional)
If you have time, it is also valuable to discuss testing strategies. Showing awareness of testing indicates a senior mindset and understanding of production quality.
Key Areas to Discuss:
- Unit Testing: Testing individual components or functions. Frameworks: Jest, Mocha.
- Integration Testing: Ensuring multiple components work together. Frameworks: React Testing Library.
- End-to-End (E2E) Testing: Simulating user flows. Frameworks: Cypress, Playwright, Selenium.
- A/B Testing: Testing variations to improve user engagement or UX.
- Performance Testing: Ensuring frontend performs well under load.
- Accessibility Testing: Automated tools (axe, Lighthouse) and manual testing.
Highlighting testing strategies shows your awareness of production readiness, and your ability to think beyond just code functionality.
Testing (Optional)
If you have time, it is also valuable to discuss testing strategies. Showing awareness of testing indicates a senior mindset and understanding of production quality.
Key Areas to Discuss:
- Unit Testing: Testing individual components or functions. Frameworks: Jest, Mocha.
- Integration Testing: Ensuring multiple components work together. Frameworks: React Testing Library.
- End-to-End (E2E) Testing: Simulating user flows. Frameworks: Cypress, Playwright, Selenium.
- A/B Testing: Testing variations to improve user engagement or UX.
- Performance Testing: Ensuring frontend performs well under load.
- Accessibility Testing: Automated tools (axe, Lighthouse) and manual testing.
Highlighting testing strategies shows your awareness of production readiness, and your ability to think beyond just code functionality.
By extending RADIO with practical examples, scaling concerns, and developer experience considerations, you’ll demonstrate both breadth and depth of frontend design expertise.