Frontend System Design Interview Questions
Short answer
A frontend system design round asks you to architect a feature — a feed, an autocomplete, a chat app — in 45 minutes. Unlike backend system design, the constraints are the network, the device and the browser. What is scored is whether you gather requirements, justify trade-offs, and remember the non-functional work.
| Backend system design | Frontend system design | |
|---|---|---|
| Main constraint | Throughput and storage | Network latency and device CPU |
| Scaling question | More users | More data on one screen |
| Typical artefacts | Services, queues, shards | Components, state, caching, transport |
| Failure to plan for | Node loss | Offline, slow 3G, low-end Android |
| Always in scope | Consistency | Accessibility and i18n |
| The give-away answer | "Add a cache" | "Virtualise the list" |
What is a frontend system design round actually assessing?
Whether you can take an ambiguous product request and turn it into an architecture, out loud, with reasons. There is no single right answer and the interviewer knows the problem cannot be solved properly in 45 minutes — so what gets scored is the process: did you scope the problem, name the constraints, propose something coherent, and explain what you traded away and why. A candidate who designs a beautiful system without ever asking who uses it usually scores worse than one with a simpler design and sharper questions.
The second thing being assessed is breadth. Backend rounds go deep on one axis; frontend rounds check whether you remember that a feature has a rendering strategy, a data-fetching strategy, a state model, an accessibility story, an internationalisation story, an error and offline story, and a way to tell whether it is working in production. Most candidates cover the first three and stop, which is exactly where the interviewer starts probing.
What framework should you use to structure the answer?
Have one and use it every time, so the structure costs you no thought under pressure. The shape below fits 45 minutes and covers what the rubric usually lists. Say the headings out loud as you move through them — it tells the interviewer where you are and makes it easy for them to redirect you if they want depth somewhere specific.
- Requirements (5 min) — who uses it, on what device, what must it do, what is explicitly out of scope.
- Constraints and scale — how many items on screen, how fresh must data be, offline, which markets and languages.
- High-level architecture — the component tree, where state lives, the API shape you want.
- Rendering strategy — CSR, SSR, SSG or ISR, and why this page is that one.
- Data and caching — fetching, invalidation, optimistic updates, pagination.
- Deep dive — the interviewer picks one area; go as deep as they want.
- Non-functional — accessibility, i18n, error and offline states, performance budget, observability.
- Wrap up — what you would build first, what you deliberately left out.
Two or three minutes on requirements feels like a long time when the clock is running, and it is the highest-return part of the round. Ask whether this is for mobile web on patchy networks or an internal desktop tool, because that single answer changes almost every decision that follows. Write the answers down where the interviewer can see them, then refer back when you justify a choice.
How do you design a news feed?
The classic prompt. The core problems are pagination, list performance and freshness. Use cursor-based pagination rather than offsets, because a page-number query breaks when items are inserted while the user scrolls — they see duplicates or skip rows. Virtualise the list so the DOM only holds what is visible, and reserve space for images so the layout does not shift as they load.
1// Cursor, not offset: stable when new posts arrive mid-scroll
2// GET /api/feed?cursor=eyJpZCI6MTIzfQ&limit=20
3// → { items: [...], nextCursor: "eyJpZCI6MTQzfQ" }
4
5const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
6 queryKey: ["feed"],
7 queryFn: ({ pageParam }) => getFeed({ cursor: pageParam }),
8 getNextPageParam: (last) => last.nextCursor,
9});
10
11// Only render what is on screen — 10,000 posts, ~15 DOM nodes
12const rows = useVirtualizer({
13 count: allItems.length,
14 estimateSize: () => 320,
15 overscan: 5,
16});
17// → memory and paint cost stop growing with the feed lengthFreshness is the interesting trade-off and worth raising yourself. Polling is simple and cheap to build but wasteful and always slightly stale; a websocket is live but adds connection management, reconnection with backoff, and server cost. A middle path many products choose is polling for the count of new items and letting the user pull them in with a "3 new posts" button, which avoids yanking content under someone's cursor.
How do you design an autocomplete or typeahead?
This prompt is about network discipline. Debounce input so you are not firing a request per keystroke, cancel the in-flight request when a new one starts, and cache results by query so backspacing is instant. The bug interviewers look for is the race condition: without cancellation, a slow response for a short prefix can arrive after a fast one for a longer prefix and overwrite the correct results.
1const cache = new Map();
2
3async function search(q, signal) {
4 if (cache.has(q)) return cache.get(q); // instant on backspace
5 const res = await fetch(`/api/search?q=${q}`, { signal });
6 const data = await res.json();
7 cache.set(q, data);
8 return data;
9}
10
11// In the component: one effect owns the timer AND the controller
12useEffect(() => {
13 const c = new AbortController();
14 const id = setTimeout(() => search(q, c.signal).then(setResults), 250);
15 return () => { clearTimeout(id); c.abort(); };
16}, [q]);
17// → at most one request per pause, and a stale response can never
18// overwrite a newer one because it was cancelledThen raise the parts nobody mentions. Keyboard navigation with arrow keys and Enter, `aria-activedescendant` so screen readers announce the highlighted option, a minimum query length so you do not search on one character, and what happens when there are no results. If the dataset is small and static, say that you would ship it to the client and search locally — recognising when the network is unnecessary is a strong answer, not a lazy one.
This is 1 of 200+ questions in the Complete Frontend Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the Complete Frontend Kit → ₹499How do you choose a rendering strategy?
Decide per route, and justify it from the user rather than from fashion. Public pages that must be indexed and are the same for everyone should be statically generated and served from a CDN. Public pages that are personalised or genuinely live need server rendering. Anything behind a login has no SEO requirement, so client rendering is fine and gives you instant in-app navigation afterwards.
The follow-up is almost always hydration. Server rendering improves the time to first paint but not the time to interactive, because the bundle still has to download and attach. That is what Server Components change: a component that runs only on the server ships no JavaScript at all, so the interactive islands are the only thing hydrated. Being able to state that distinction — faster paint versus less JavaScript — is what a strong answer sounds like.
1// app/blog/[slug]/page.tsx — static, rebuilt hourly
2export const revalidate = 3600;
3
4// app/search/page.tsx — unique per query, still indexable
5export const dynamic = "force-dynamic";
6
7// app/dashboard/page.tsx — private, so SEO is irrelevant
8"use client";
9// → one project, three strategies, each justified by the user
10// rather than by a framework defaultHow do you handle data fetching, caching and mutations?
Separate server state from client state and say so explicitly, because it reframes the whole question. Server state is a cache of something that lives elsewhere and needs invalidation, refetching and deduplication — that is React Query or SWR, not Redux. Client state is what your UI owns: which modal is open, which tab is active, what is typed in a form.
1const { mutate } = useMutation({
2 mutationFn: likePost,
3
4 onMutate: async (postId) => {
5 await queryClient.cancelQueries({ queryKey: ["feed"] });
6 const previous = queryClient.getQueryData(["feed"]);
7 queryClient.setQueryData(["feed"], (old) => bumpLike(old, postId));
8 return { previous }; // handed to onError
9 },
10
11 onError: (_err, _id, ctx) => {
12 queryClient.setQueryData(["feed"], ctx.previous); // roll back
13 },
14
15 onSettled: () => queryClient.invalidateQueries({ queryKey: ["feed"] }),
16});
17// → the heart fills instantly; a failed request restores the old
18// state instead of leaving a lie on screenOptimistic updates are worth raising unprompted for anything with a like, a follow or a toggle, because the perceived latency difference is enormous. The part that earns the mark is the rollback: an optimistic update with no error path is a bug that shows users something that never happened. Say both halves in the same sentence and you have answered the follow-up before it arrives.
When do you use polling, SSE or WebSockets?
Pick by direction and frequency. Polling suits data that changes on the order of minutes and needs no infrastructure — a notification count is a good fit. Server-Sent Events are one-directional server-to-client over plain HTTP, with automatic reconnection built in, which makes them ideal for live scores, progress and streamed responses. WebSockets are the answer when the client also sends frequently: chat, collaborative editing, multiplayer.
| Need | Use | What it costs you |
|---|---|---|
| Occasional freshness | Polling | Wasted requests, always slightly stale |
| Server pushes, client mostly reads | SSE | One-directional; connection limits on HTTP/1.1 |
| Both directions, frequently | WebSocket | Reconnection, backoff, auth, sticky sessions |
| Streaming an AI response | SSE or streamed fetch | Backpressure and partial-render handling |
| Live cursors, high frequency | WebSocket + throttling | Bandwidth if you do not batch |
Whichever you choose, describe the failure path. A websocket design that does not mention exponential backoff on reconnect, buffering messages while disconnected, and what the UI shows when the connection drops is incomplete, and that is usually where the interviewer takes the deep dive.
1let attempt = 0;
2
3function connect() {
4 const ws = new WebSocket(URL);
5
6 ws.onopen = () => { attempt = 0; flushQueued(); };
7
8 ws.onclose = () => {
9 // Exponential backoff with jitter, capped — without the cap a
10 // long outage means minute-long gaps; without jitter every client
11 // reconnects in lockstep and stampedes the server.
12 const wait = Math.min(1000 * 2 ** attempt, 30_000) * (0.5 + Math.random());
13 attempt += 1;
14 setTimeout(connect, wait);
15 };
16
17 ws.onerror = () => ws.close();
18 return ws;
19}
20// → 1s, 2s, 4s, 8s… capped at 30s, spread across clientsWhat non-functional requirements do candidates forget?
This is the easiest place to differentiate yourself, because most candidates never get here. Bring them up yourself in the last five minutes even if nobody asks — it demonstrates that you have shipped things rather than only designed them.
- →Accessibility — keyboard paths, focus management on route change, announced live regions, contrast.
- →Internationalisation — text expansion, right-to-left layouts, locale-aware dates and numbers.
- →Performance budget — a JavaScript size limit and Core Web Vitals targets, checked in CI.
- →Error and empty states — every async surface has at least four: idle, loading, empty, error.
- →Offline and flaky networks — what a failed request looks like, and whether retry is safe.
- →Observability — error tracking, real-user monitoring, and a feature flag to turn it off.
- →Security — XSS from user content, token storage, and what goes in the URL.
- →Testing strategy — what is unit tested, what needs an end-to-end test, what is not worth testing.
India-specific constraints are worth naming if the product serves an Indian audience, because it shows you are designing for real users rather than for your laptop. Mid-range Android devices with slow CPUs make JavaScript execution — not download — the bottleneck, patchy connectivity makes offline handling a real requirement rather than a nicety, and data cost makes image strategy a product decision.
How do you design a component library or design system?
A different flavour of prompt that comes up for senior roles. The core tension is flexibility versus consistency: too rigid and teams work around you, too flexible and there is no system. Talk about design tokens as the single source of truth, a composable component API rather than a prop for every variation, and how you version and ship breaking changes.
1// Rigid: every new need means a new prop, forever
2<Card title="x" subtitle="y" showAvatar avatarSrc="…" footerButtonText="Go" />
3
4// Composable: the consumer arranges the parts
5<Card>
6 <Card.Header>
7 <Avatar src="…" />
8 <Card.Title>x</Card.Title>
9 </Card.Header>
10 <Card.Footer><Button>Go</Button></Card.Footer>
11</Card>
12// → new layouts need no change to Card at all, and the API
13// stops growing one boolean at a timeThen cover the operational side, which is what actually makes a design system succeed or fail: documentation people can copy from, a visual regression test suite, semantic versioning with a codemod for breaking changes, and a contribution path so product teams can add to it instead of forking. A candidate who talks about adoption and migration rather than only about components is answering the real question.
How is this different from a backend system design round?
The constraints move. Backend rounds are about throughput, storage and consistency across machines; frontend rounds are about latency, device CPU and the browser. "Scale" usually means more data on one screen rather than more users, which is why virtualisation, bundle size and caching dominate the conversation.
Should I draw diagrams?
Yes — a component tree and a data-flow sketch make your design far easier to follow, and they give the interviewer something to point at when they want a deep dive. Keep them rough; nobody is grading the boxes. Talking while you draw is what makes them useful.
What if I don't know a specific technology they mention?
Say so, then reason from first principles about what it would need to do. "I haven't used that, but for this I'd need something that gives me X and Y — is that roughly what it does?" is a good answer. Bluffing is the worst outcome, because the follow-up will find it immediately.
How much code should I write?
Very little. This round is about architecture, and time spent typing is time not spent explaining. A short snippet to make a specific point — a cache key shape, an optimistic update — is useful; implementing a component is not what is being measured.
How do you handle a very large list of items?
Virtualise so the DOM holds only what is visible, paginate with cursors so the payload stays bounded, and reserve space for media so nothing shifts as it loads. If items vary in height, mention dynamic measurement, because that is the usual follow-up and it is the hard part.
Frequently asked questions
- What is a frontend system design interview?
- A 45-minute round where you architect a user-facing feature — a feed, an autocomplete, a chat app, a design system — rather than writing code. You are assessed on how you gather requirements, structure an architecture, justify trade-offs, and whether you remember non-functional concerns like accessibility and error handling.
- How is frontend system design different from backend system design?
- The constraints are the network, the device and the browser rather than throughput and storage. Scaling usually means more data on one screen instead of more users, so virtualisation, bundle size, caching and rendering strategy dominate where sharding and queues would in a backend round.
- What framework should I use to answer?
- Requirements, constraints, high-level architecture, rendering strategy, data and caching, a deep dive on whatever the interviewer picks, non-functional requirements, then a wrap-up of what you would build first. Say the headings out loud so the interviewer can redirect you.
- What are the most common frontend system design questions?
- Design a news feed, an autocomplete, a chat application, an infinite-scroll gallery, a collaborative editor, a dashboard with live data, or a component library. The feed and the autocomplete are the two most frequently asked because they cover pagination, caching and race conditions.
- How do you design an infinite scroll feed?
- Cursor-based pagination so inserts do not cause duplicates or skips, list virtualisation so the DOM size stays constant, IntersectionObserver on a sentinel to trigger loading, reserved space for images to avoid layout shift, and a decision about freshness — polling, a websocket, or a "new posts" button.
- How do you prevent race conditions in a search box?
- Cancel the previous request with AbortController whenever a new one starts. Debouncing alone only reduces how many requests fire; it does not stop a slow earlier response arriving after a faster later one and overwriting the correct results. Caching by query makes backspacing instant as well.
- When should you use WebSockets instead of polling?
- When the client sends frequently too, and latency matters — chat, collaborative editing, multiplayer. For server-to-client updates only, Server-Sent Events are simpler and reconnect automatically. For data that changes every few minutes, polling costs nothing to build and is usually enough.
- What are optimistic updates?
- Updating the UI immediately as though a mutation succeeded, then reconciling with the server response. They make likes and toggles feel instant. The essential half is the rollback: on failure you must restore the previous state, or the interface is showing something that never happened.
- How do you handle a very long list of items in the DOM?
- Virtualise it — render only the rows in the viewport plus a small overscan, so the DOM node count stays constant no matter how many items exist. Combine with cursor pagination to bound the payload. Variable-height rows need dynamic measurement, which is the usual follow-up question.
- What non-functional requirements should I mention?
- Accessibility, internationalisation, a performance budget with Core Web Vitals targets, error and empty states, offline behaviour, observability through error tracking and real-user monitoring, security around user content and tokens, and a testing strategy. Most candidates never reach these, so raising them yourself is the cheapest way to stand out.
- How do you prepare for a frontend system design round?
- Practise the same framework out loud on five or six prompts until the structure is automatic, and build a real opinion on each recurring decision — rendering strategy, state management, transport, caching. Reading about them is not enough; you need to be able to defend a choice under a follow-up question.
- Do I need to know backend concepts?
- Enough to talk to the API sensibly — REST versus GraphQL, pagination shapes, caching headers, authentication flows, and roughly what makes an endpoint slow. You are not expected to design the database, but a candidate who cannot discuss the contract they are consuming is limited.
This is 1 of 200+ questions in the Complete Frontend Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the Complete Frontend Kit → ₹499