Ch. 12

System Design

System design rounds with a frontend lens: component architecture, data fetching, caching, performance and trade-offs.

56 interview questions34 quiz questions1 notes
your progress0%

Notes in this chapter

Filter all notes →
read ✓System Design · hard

Frontend System Design: Build an Autocomplete

A structured walkthrough of the autocomplete design round: requirements, architecture, race-free fetching, caching, rendering, the ARIA combobox and metrics.

~7 min readread →

Top 56 System Design interview questions most asked first

  1. 1.How do you approach a frontend system design interview? Walk me through the steps you follow.easy

    I use a repeatable framework and talk through trade-offs as I go:

    • Requirements: clarify core features, users, devices and scale, plus non-functional needs (performance, accessibility, offline, i18n, SEO). Agree on what is out of scope.
    • Architecture: sketch the main components, where rendering happens (CSR, SSR, SSG) and how the client talks to the server.
    • Data model: the client-side entities, and what lives in the server cache versus local UI state.
    • API and interfaces: endpoints or queries, pagination, the real-time channel, and key component props.
    • Deep dives: performance, accessibility, security, error and empty states, edge cases.

    I timebox each step, check with the interviewer which area to go deep on, and always state the trade-off behind a choice instead of just naming a library.

    What interviewers listen for
    • Clarify functional and non-functional requirements first
    • High-level component architecture and rendering strategy
    • Client data model and API contract
    • Deep dives: performance, accessibility, security, edge cases
    • Timebox steps and explain trade-offs aloud

    Likely follow-up: Which non-functional requirements matter most for a mobile-first product?

  2. 2.Design the frontend for a news feed like the one on a social network.hard

    Requirements: an endless feed of posts (text, images, video), like/comment/share, a composer, and new posts arriving while the user reads. Non-functional: fast first load, smooth scrolling on low-end phones, accessible.

    • Architecture: SSR or streamed first page for a fast LCP; FeedList, PostCard, a code-split Composer and a MediaViewer.
    • API: GET /feed?cursor=&limit= returning { items, nextCursor }; cursor pagination avoids duplicates when new posts land at the top.
    • State: a server-state cache with posts and authors normalized by id, so a like updates the post everywhere; optimistic likes with rollback.
    • Performance: an IntersectionObserver sentinel prefetches the next page early, virtualization bounds the DOM, and images are responsive (srcset) and lazy-loaded below the fold.
    • Real time: SSE or a WebSocket buffers new posts; show a "New posts" pill instead of shifting content (no CLS).
    • Accessibility: the ARIA feed pattern with article items, aria-setsize and aria-posinset, and full keyboard support.
    • Edge cases: dedupe across pages, retry failed pages, empty and error states, scroll restoration on back navigation.
    interface Post {
      id: string;
      authorId: string; // users live in their own normalized map
      text: string;
      media: { url: string; width: number; height: number }[];
      likeCount: number;
      likedByMe: boolean;
      createdAt: string; // ISO 8601
    }
    interface FeedPage { items: Post[]; nextCursor: string | null }
    What interviewers listen for
    • Clarify functional and non-functional requirements
    • Cursor pagination with a prefetching sentinel
    • Normalized client cache and optimistic updates
    • Virtualization and responsive lazy-loaded images
    • New-posts pill instead of layout shift; ARIA feed pattern

    Likely follow-up: How would you handle new posts arriving in real time? · How do you restore scroll position when the user navigates back?

  3. 3.Design an autocomplete (typeahead) search component, like the search box on Google or Amazon.mid

    Requirements: suggestions appear as the user types, keyboard and mouse selection, highlighted matches, recent searches; it should feel instant and be reusable.

    • Components: Input, SuggestionList and SuggestionItem, configured with props like fetchSuggestions, minChars, debounceMs and renderItem.
    • API: GET /suggest?q=ne&limit=8 returning ranked items; the server owns the prefix index.
    • Network: debounce input (roughly 150–300 ms), skip very short queries, and cancel stale requests with AbortController (or ignore responses that don't match the latest query) so results never arrive out of order.
    • Caching: an in-memory LRU map with a TTL, keyed by query, makes backspacing instant; small static datasets can be shipped and searched on the client.
    • Accessibility: the ARIA combobox pattern: role="combobox" with aria-expanded, aria-controls and aria-activedescendant, a listbox of option elements, arrow keys, Enter and Escape, and a polite live region announcing the result count.
    • Edge cases: no results, errors and offline, IME composition, and highlighting matches by splitting text rather than injecting HTML (XSS).
    What interviewers listen for
    • Debounce input and enforce a minimum query length
    • Cancel or ignore stale responses to avoid races
    • Cache results per query in memory
    • ARIA combobox pattern with full keyboard support
    • Handle empty, error, IME and XSS-safe highlighting

    Likely follow-up: How would you merge suggestions from several sources, such as recent searches and products?

  4. 4.Compare CSR, SSR, SSG, ISR, streaming SSR, islands and React Server Components. How do you choose a rendering strategy?mid
    • CSR: the server sends a shell and JavaScript renders everything. Rich interactivity and cheap hosting, but a slower first paint and weaker SEO.
    • SSR: HTML is rendered per request, so content appears early and is crawlable; it costs server time (TTFB) and still needs hydration.
    • SSG: pages are rendered at build time and served from a CDN; fastest and cheapest, but only as fresh as the last build.
    • ISR: static pages regenerated in the background after a revalidation window or on demand.
    • Streaming SSR: the server flushes the shell first and streams slower sections as their data resolves (Suspense boundaries).
    • Islands: static HTML with small interactive components hydrated independently (Astro).
    • RSC: components that run only on the server and ship no JavaScript; only client components hydrate.

    Choose per route: SSG or ISR for marketing and docs, SSR or streaming for personalized SEO pages, CSR for app screens behind a login.

    What interviewers listen for
    • CSR: browser renders; slower first paint, weaker SEO
    • SSR and streaming: per-request HTML, better LCP, server cost
    • SSG and ISR: build-time or background-regenerated pages
    • Islands and RSC reduce client JavaScript and hydration
    • Choose per route by freshness, SEO and interactivity

    Likely follow-up: What problems does hydration introduce, and how do islands or RSC help?

  5. 5.What are the Core Web Vitals, what are their "good" thresholds, and how would you improve each one?mid

    Core Web Vitals are Google's user-centric metrics, assessed at the 75th percentile of real page loads:

    • LCP (Largest Contentful Paint), loading: good is 2.5 s or less. Cut TTFB (CDN, caching), preload the LCP image or give it fetchpriority="high", never lazy-load it, and remove render-blocking CSS and JS.
    • INP (Interaction to Next Paint), responsiveness: good is 200 ms or less; it replaced FID in March 2024. Break up long tasks, yield to the main thread, ship less JavaScript, and move heavy work to Web Workers.
    • CLS (Cumulative Layout Shift), visual stability: good is 0.1 or less. Set width/height or aspect-ratio on media, reserve space for ads and embeds, avoid inserting content above existing content, and match fallback font metrics.

    Measure in the field (RUM, CrUX) and debug in the lab (Lighthouse, DevTools).

    What interviewers listen for
    • LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1
    • Judged at the 75th percentile of field data
    • LCP: faster TTFB, prioritize the hero image
    • INP: break up long tasks, less main-thread JS
    • CLS: reserve space for media, ads and fonts

    Likely follow-up: How does INP differ from First Input Delay, the metric it replaced?

  6. 6.Design an infinite scrolling list that can grow to thousands of items, such as search results or a product feed.mid

    Requirements: load more items as the user nears the end, stay smooth with thousands of rows, and handle loading, error and end-of-list states.

    • Trigger: an IntersectionObserver on a sentinel element near the bottom, with a rootMargin so the next page is fetched before the user gets there; no scroll-event polling.
    • API: cursor pagination, GET /items?cursor=abc&limit=30 returning { items, nextCursor }; cursors stay stable when items are inserted, unlike offsets.
    • State: pages cached by query key (for example an infinite query in TanStack Query), deduped by id, with only one request in flight.
    • Performance: virtualize so only visible rows plus an overscan buffer are in the DOM; reserve image dimensions to avoid layout shift.
    • Accessibility: announce loading in a polite live region, keep focus stable, and offer a "Load more" button, since pure infinite scroll can make the footer unreachable.
    • Edge cases: a failed page with inline retry, empty results, the last page, and restoring scroll position and loaded pages on back navigation.
    What interviewers listen for
    • IntersectionObserver sentinel with rootMargin prefetch
    • Cursor-based pagination with deduplication
    • Virtualize rows to bound DOM size
    • Loading, error, retry and end-of-list states
    • Load-more fallback and scroll restoration

    Likely follow-up: How would you restore the exact scroll position when the user returns from a detail page?

  7. 7.Design a chat or messaging web app like Slack or WhatsApp Web.hard

    Requirements: 1:1 and group conversations, real-time send and receive, history, typing indicators, read receipts, attachments; it must survive flaky networks.

    • Transport: a WebSocket for real-time events (reconnect with exponential backoff and jitter, heartbeats) plus REST for history and uploads; share one socket across tabs via a SharedWorker or leader election.
    • Data model: conversations and messages normalized by id; each message has a client-generated id, a server sequence number and a status (sending, sent, delivered, read, failed).
    • Sending: optimistic insert into an outbox persisted in IndexedDB, retry on failure, dedupe by client id when the server acknowledges.
    • Sync: order by server sequence; after reconnecting, fetch everything since the last seen sequence to fill gaps.
    • Message list: reverse infinite scroll that keeps the scroll position when older pages are prepended, sticks to the bottom only if the user is already there, and virtualizes long histories.
    • Accessibility: role="log" (an implicitly polite live region) for incoming messages, a labelled composer, keyboard shortcuts.
    • Extras: unread counts, notifications when the tab is hidden, sanitized message rendering.
    type MessageStatus = 'sending' | 'sent' | 'delivered' | 'read' | 'failed';
    interface Message {
      clientId: string; // generated on the client, used to dedupe acks
      id?: string; // assigned by the server
      seq?: number; // server ordering within the conversation
      conversationId: string;
      senderId: string;
      body: string;
      status: MessageStatus;
      createdAt: string;
    }
    What interviewers listen for
    • WebSocket with reconnect, backoff and heartbeats
    • Optimistic send with client ids and a persisted outbox
    • Server sequence numbers for ordering and gap sync
    • Reverse infinite scroll that preserves scroll position
    • Message statuses, unread counts, notifications

    Likely follow-up: How would you keep several open tabs of the same account in sync? · How would end-to-end encryption change the design?

  8. 9.How do you decide where state should live in a large frontend app: local, global, server or URL state?mid

    I classify state by who owns it and how long it lives:

    • Local UI state: open/closed, input drafts, hover. Keep it in the component and colocate it as low as possible.
    • Server state: data owned by the backend (users, products). It is a cache, not app state, so use a data-fetching library (TanStack Query, SWR, Apollo) for deduping, background refetching, invalidation and retries. Don't copy it into a global store.
    • Global client state: truly app-wide, client-owned values such as theme, session or a draft cart. Context works for rarely changing values; a store (Redux Toolkit, Zustand) suits frequent updates.
    • URL state: filters, sort, page, selected tab. Keeping it in the URL makes views shareable and the back button work.
    • Form state: a form library with schema validation.

    Derive instead of duplicating, keep one source of truth, and lift state only when siblings need it.

    What interviewers listen for
    • Local state stays colocated in components
    • Server state belongs in a query cache, not a store
    • Global store only for app-wide client-owned state
    • Filters, sort and pagination belong in the URL
    • Derive rather than duplicate state

    Likely follow-up: When does React context become a performance problem, and what do you do about it?

  9. 10.Compare short polling, long polling, Server-Sent Events and WebSockets for real-time updates. When would you pick each?mid
    • Short polling: a request every N seconds. Simple and universal, but wasteful and up to N seconds stale. Fine for slow-changing data like a build status.
    • Long polling: the server holds the request open until there is data or a timeout, then the client reconnects. Near real time over plain HTTP, but every message costs a new request.
    • SSE: EventSource keeps one HTTP response open while the server streams text/event-stream messages. Server-to-client only, UTF-8 text, automatic reconnection with Last-Event-ID. Over HTTP/1.1 browsers cap it at 6 connections per domain, so prefer HTTP/2.
    • WebSockets: a full-duplex connection upgraded from HTTP. Best for chat, multiplayer and collaborative editing, but you handle reconnection, heartbeats, auth and stateful scaling (sticky sessions or pub/sub).

    Default to SSE when only the server pushes (notifications, live feeds, dashboards); use WebSockets when the client also sends frequent messages.

    What interviewers listen for
    • Short polling: simple but wasteful and laggy
    • Long polling: held request, reconnect after each message
    • SSE: one-way server push with auto-reconnect
    • WebSockets: bidirectional; you manage reconnection and scaling
    • Choose by message direction and frequency

    Likely follow-up: How would you detect and recover from a silently dropped WebSocket connection?

  10. 11.Walk me through the caching layers available to a frontend. How would you set cache headers for HTML, hashed assets and API responses?hard

    From closest to the user outward:

    • In-memory: app-level caches such as a query cache or memoized selectors; fastest, but lost on reload.
    • Service worker: the Cache API with strategies like cache-first or stale-while-revalidate; enables offline use.
    • HTTP cache: controlled by Cache-Control (max-age, no-cache = store but revalidate before use, no-store = never store, private, immutable) plus validators (ETag with If-None-Match, Last-Modified) that allow cheap 304 Not Modified responses.
    • CDN/edge: a shared cache that honors s-maxage and stale-while-revalidate and offers purge APIs.
    • Server: application or data caches such as Redis.

    Typical setup: fingerprinted assets (app.3f9a1c.js) get public, max-age=31536000, immutable; HTML gets no-cache so a new deploy is picked up immediately; personalized API responses use private with short lifetimes or revalidation. Invalidate by changing URLs rather than waiting for caches to expire.

    What interviewers listen for
    • Layers: memory, service worker, HTTP cache, CDN, server
    • Hashed assets: long max-age plus immutable
    • HTML: no-cache so deploys propagate immediately
    • ETag and Last-Modified enable 304 revalidation
    • private vs public; s-maxage for shared caches

    Likely follow-up: What is the difference between no-cache and no-store?

  11. 12.Users report that a key page feels slow. How would you investigate and fix it?hard

    Measure before changing anything:

    • Field data first: RUM or CrUX shows which metric is failing (LCP, INP or CLS) and on which pages, devices and networks, at the 75th percentile.
    • Reproduce in the lab: Lighthouse and the DevTools Performance panel, with CPU and network throttling that match real users.
    • Break the metric down: LCP splits into TTFB, resource load delay, resource load duration and element render delay; INP into input delay, processing duration and presentation delay. Attack the largest part.
    • Common fixes: CDN and caching for TTFB, prioritizing the hero image, removing render-blocking resources, code splitting and trimming bundles (check with a bundle analyzer), deferring third-party scripts, breaking up long tasks, and cutting unnecessary re-renders (React Profiler).
    • Verify and guard: compare before and after in RUM, then add performance budgets to CI so the regression doesn't return.
    What interviewers listen for
    • Start with field data to find the failing metric
    • Reproduce in the lab with throttling
    • Break LCP and INP into their sub-parts
    • Fix the biggest bottleneck: TTFB, images, JS, long tasks
    • Verify in RUM and add budgets to CI

    Likely follow-up: How would you find out which third-party script is hurting INP?

  12. 13.Design an e-commerce product listing page with filters, sorting and pagination.mid

    Requirements: product grid, faceted filters (category, brand, price, rating), sorting, pagination and shareable URLs; SEO and a fast LCP matter because this page drives revenue.

    • Rendering: SSR or ISR for crawlable HTML and a fast first paint, hydrating only interactive parts like the filters.
    • URL as state: ?brand=nike&price=50-100&sort=price_asc&page=2 is the source of truth, so links are shareable and Back works; canonical tags stop filter combinations from creating duplicate SEO pages.
    • API: GET /products?... returning { items, facets, total }, where facets carry counts so options that would yield nothing can be disabled.
    • State and caching: responses cached by query key, the next page prefetched, and previous results kept visible while new ones load.
    • Interaction: apply checkboxes immediately, debounce the price slider, and abort stale requests so rapid changes can't render out of order.
    • Performance: responsive images from an image CDN, fixed-size skeletons, lazy loading below the fold.
    • Accessibility: filters as fieldset/legend groups, the result count announced in a live region, the mobile filter drawer built as a real dialog.
    • Edge cases: zero results with a clear-filters action, out-of-stock items, price changes.
    What interviewers listen for
    • SSR or ISR for SEO and a fast LCP
    • Filters, sort and page encoded in the URL
    • API returns items, facet counts and total
    • Abort stale requests; keep previous results while loading
    • Accessible filter groups and announced result count

    Likely follow-up: Would you choose offset or cursor pagination here, and why?

  13. 14.Design a notifications system for a web app: a bell with an unread count, a notification list, and push notifications.hard

    Requirements: unread badge, paginated list, mark as read, real-time delivery while the app is open, push while it is closed, user preferences.

    • Data model: { id, type, actorIds, targetUrl, createdAt, readAt }; the server aggregates similar events ("Ana and 3 others liked your post").
    • API: GET /notifications?cursor=, GET /notifications/unread-count, and POST /notifications/read taking ids or "all".
    • Real time: SSE (one-way is enough) or an existing WebSocket pushes new items and count changes, with polling as a fallback; after reconnecting, fetch everything since the last seen id.
    • Multi-tab: sync the count and read state across tabs with BroadcastChannel.
    • Push: a service worker using the Push API and Notification API. Ask for permission in context after a user action, never on page load, and handle the denied state. On iOS, web push only works for web apps added to the Home Screen.
    • Accessibility: a bell button labelled like "Notifications, 3 unread", a polite live region used sparingly, and a keyboard-navigable panel.
    • Edge cases: dedupe, optimistic mark-as-read with rollback, links to deleted content, preferences and quiet hours.
    What interviewers listen for
    • Paginated list plus a separate unread-count endpoint
    • SSE or WebSocket for live updates, polling fallback
    • Sync read state across tabs
    • Web Push via service worker; ask permission in context
    • Aggregation, dedupe and an accessible badge label

    Likely follow-up: How would you avoid showing the same notification in several open tabs?

  14. 15.What is the difference between debouncing and throttling, and where would you use each in a UI?easy

    Both limit how often a function runs in response to rapid events.

    • Debounce waits until events stop for N milliseconds, then runs once. Use it when only the final value matters: search-as-you-type requests, autosave, field validation, or recalculating layout after a resize ends.
    • Throttle runs at most once every N milliseconds while events keep firing. Use it for continuous feedback: tracking scroll position, drag handling, mousemove, or rate-limiting analytics events.

    Options matter: a leading call fires immediately (useful against double-clicks), a trailing call captures the last event. For visual updates tied to scrolling or pointer movement, requestAnimationFrame is often better than a time-based throttle because it syncs with frames. Cancel pending timers on unmount, and remember that debouncing doesn't fix out-of-order responses; you still need to cancel stale requests.

    What interviewers listen for
    • Debounce: run once after events stop
    • Throttle: run at most once per interval
    • Debounce search and autosave; throttle scroll and drag
    • Leading vs trailing edge options
    • requestAnimationFrame for per-frame visual updates

    Likely follow-up: Implement a debounce function that also has a cancel method.

  15. 16.What is the difference between offset-based and cursor-based pagination, and which would you use for a feed?easy

    Offset pagination (?page=3&limit=20, SQL OFFSET 40) lets users jump to any page and see totals, which suits admin tables and search results with page numbers. The downsides: if items are inserted or deleted while the user paginates, they see duplicates or miss items, and large offsets get slow because the database still walks past the skipped rows.

    Cursor pagination (?after=eyJpZCI6MTIzfQ&limit=20) returns an opaque cursor pointing at the last item seen, usually encoding the sort key plus a unique tiebreaker such as createdAt and id. It stays stable while data changes and uses an index efficiently, but you can't jump to page 7 and totals are harder.

    For a feed or infinite scroll I use cursors; for a table where users need page numbers I use offsets.

    What interviewers listen for
    • Offset: random page access and totals
    • Offset: duplicates or gaps when data changes; slow when deep
    • Cursor: opaque pointer to last item, stable and index-friendly
    • Cursor cannot jump to arbitrary pages
    • Cursors for feeds, offsets for numbered tables

    Likely follow-up: What should a cursor encode if many items share the same timestamp?

  16. 17.Design a nested comment thread like the ones on Reddit or Hacker News.mid

    Requirements: replies of arbitrary depth, collapse/expand, inline reply, edit and delete, sorting (top, new), votes; threads can hold thousands of comments.

    • Data model: store comments flat and normalized, { id, parentId, authorId, body, score, childIds, replyCount, deleted }, instead of a deeply nested tree, so updating one comment doesn't mean rebuilding the tree.
    • API: GET /posts/:id/comments?sort=top&cursor= for top-level comments with their first few replies, and GET /comments/:id/replies?cursor= behind "View 12 more replies".
    • Rendering: a recursive Comment component with a maximum indentation depth, beyond which a "Continue thread" link opens that subtree. For huge threads, flatten the visible nodes into a list with depth and virtualize it.
    • State: collapsed ids and drafts are local UI state; posting and voting are optimistic with rollback.
    • Security: render user Markdown through a sanitizer, never raw HTML.
    • Accessibility: nested lists, collapse buttons with aria-expanded, clear author and time labels, keyboard access to reply.
    • Edge cases: deleted parents shown as "[deleted]" so replies keep their context, deep threads on mobile, edits racing with replies.
    What interviewers listen for
    • Flat, normalized comments linked by parentId
    • Lazy-load replies with per-thread cursors
    • Recursive render with a depth cap and continue link
    • Optimistic posting and voting; sanitize Markdown
    • Soft-deleted parents preserve reply context

    Likely follow-up: How would you show new replies from other users in real time without disrupting the reader?

  17. 18.What is code splitting, and how do you decide what to lazy-load?easy

    Code splitting breaks one large bundle into chunks loaded on demand, so the initial page downloads, parses and executes less JavaScript. Bundlers create a separate chunk at every dynamic import().

    • Route-based: each route is its own chunk; the biggest and safest win.
    • Component-based: heavy UI that isn't needed right away, such as modals, rich text editors, charts, maps or admin tools (React.lazy with Suspense).
    • Library-based: import large libraries only in the code path that uses them.
    • Vendor chunks: stable dependencies in their own long-cached chunk.

    Don't lazy-load anything needed for the first paint or the LCP element, and avoid many tiny chunks that create request waterfalls. Hide the latency by prefetching likely next chunks on hover, on idle, or when a link enters the viewport, and verify the result with a bundle analyzer.

    What interviewers listen for
    • Dynamic import() creates on-demand chunks
    • Split by route first, then heavy components
    • Never lazy-load above-the-fold or LCP content
    • Prefetch likely next chunks on hover or idle
    • Measure with a bundle analyzer; avoid waterfalls

    Likely follow-up: How would you handle a chunk that fails to load after a new deploy?

  18. 19.What are the main security threats to a frontend application, such as XSS, CSRF and clickjacking, and how do you defend against them?hard
    • XSS: attacker-controlled script runs in your origin and can steal data or act as the user. Rely on framework escaping, avoid innerHTML and dangerouslySetInnerHTML (sanitize with DOMPurify when unavoidable), block javascript: URLs, and add a strict Content Security Policy based on nonces or hashes, e.g. script-src 'nonce-r4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none'. Trusted Types can lock down DOM sinks.
    • CSRF: another site triggers requests that carry the user's cookies. Use SameSite=Lax or Strict cookies, anti-CSRF tokens on state-changing requests, and Origin or Sec-Fetch-Site checks; never change state on GET.
    • Clickjacking: your page is framed invisibly to trick clicks. Send CSP frame-ancestors 'none' (or 'self'), plus X-Frame-Options: DENY for older browsers.
    • Tokens: localStorage is readable by any injected script, so prefer HttpOnly, Secure, SameSite cookies.

    Also enforce HTTPS with HSTS, use Subresource Integrity for third-party scripts, and audit dependencies.

    What interviewers listen for
    • XSS: escape output, sanitize HTML, strict nonce-based CSP
    • CSRF: SameSite cookies, CSRF tokens, Origin checks
    • Clickjacking: frame-ancestors or X-Frame-Options
    • Keep tokens out of localStorage; use HttpOnly cookies
    • HTTPS with HSTS, SRI and dependency audits

    Likely follow-up: Where would you store an access token in a single-page app, and why?

  19. 20.Design a video player for a streaming site like YouTube or Netflix.hard

    Requirements: play/pause, seeking with previews, volume, captions, quality selection, fullscreen and picture-in-picture, keyboard shortcuts, resume where you left off; smooth playback on poor networks.

    • Streaming: adaptive bitrate over HLS or DASH. Video is cut into short segments at several bitrates listed in a manifest; a player built on Media Source Extensions (hls.js, dash.js, Shaka) picks a rendition from measured bandwidth and buffer level. Safari also plays HLS natively. DRM goes through Encrypted Media Extensions, and segments come from a CDN.
    • Architecture: a native video element with custom controls, driven by a state machine (idle, loading, playing, paused, buffering, ended, error).
    • Performance: lazy-load the player code, show a poster image, use preload="metadata", and prefetch the first segments when playback is likely.
    • Accessibility: WebVTT captions via track, labelled buttons, keyboard controls (Space, arrows, F, M), visible focus. Browsers block autoplay with sound, so autoplay only when muted.
    • Analytics: quality-of-experience metrics such as startup time and rebuffering ratio.
    • Edge cases: network drops, background tabs, playsinline for inline playback on iOS, saving progress on visibilitychange.
    What interviewers listen for
    • Adaptive bitrate streaming via HLS or DASH and MSE
    • Custom controls over a player state machine
    • Poster, lazy player code, CDN-delivered segments
    • Captions, keyboard shortcuts, muted-only autoplay
    • Track startup time and rebuffering ratio

    Likely follow-up: How does the player decide when to switch to a lower quality?

  20. 21.What are optimistic updates, how do you implement rollback, and when should you avoid them?mid

    An optimistic update changes the UI immediately as if the request had already succeeded, then reconciles when the response arrives. It makes likes, toggles, reordering and sending messages feel instant.

    A typical implementation (for example with TanStack Query):

    • Before the request: cancel in-flight fetches for the affected data so they can't overwrite the change, snapshot the current cache, then apply the change, using a temporary id for new items.
    • On error: restore the snapshot and show an error with a retry option.
    • On success: replace temporary ids with server ids, then invalidate or refetch to sync with the server's truth.

    Watch for concurrent mutations on the same entity (queue them or compare versions) and make retries safe with idempotency keys. Avoid optimistic UI when failure is likely or costly, such as payments, irreversible deletes or anything that needs server validation; show a pending state instead.

    What interviewers listen for
    • Update the UI first, reconcile when the server responds
    • Snapshot cache, cancel in-flight fetches, apply change
    • Roll back on error and tell the user
    • Replace temporary ids and revalidate on success
    • Avoid for payments or likely-to-fail actions

    Likely follow-up: How do you handle two quick optimistic updates to the same item when the first one fails?

  21. 22.Explain the critical rendering path: what happens between the browser receiving HTML and the first paint, and how do you optimize it?easy

    The browser parses HTML into the DOM and CSS into the CSSOM. CSS is render-blocking: nothing paints until the stylesheets the page needs have loaded. A classic synchronous script is parser-blocking: parsing pauses while it downloads and runs. DOM and CSSOM combine into a render tree of visible nodes, then the browser runs layout (geometry), paint (pixels) and compositing (layers onto the screen).

    Optimizations:

    • Minimize critical resources: inline critical CSS, load the rest without blocking, remove unused CSS.
    • Mark scripts defer (run in order after parsing) or async (run as soon as downloaded); type="module" scripts are deferred by default.
    • Let the browser discover key resources early with preload and preconnect, and prioritize the LCP image.
    • Cut bytes and round trips with compression, a CDN, and HTTP/2 or HTTP/3.
    What interviewers listen for
    • HTML to DOM, CSS to CSSOM, then render tree
    • Layout, paint, then compositing
    • CSS blocks rendering; sync scripts block parsing
    • defer or async scripts; inline critical CSS
    • Preload and preconnect key resources

    Likely follow-up: What is the difference between async and defer?

  22. 23.Design a real-time collaborative document editor like Google Docs. How do OT and CRDTs differ?hard

    Requirements: several users edit one document at once, see each other's cursors, edits appear with low latency, offline edits merge later, and undo reverts only your own changes.

    • Editing model: a structured editor (ProseMirror, Lexical) where each change is an operation applied locally first for instant feedback, then synced.
    • OT (Operational Transformation): concurrent operations are transformed against each other ("insert at 5" becomes "insert at 8" after a remote insert earlier in the text). It usually relies on a central server to order operations; Google Docs is built on OT.
    • CRDTs (Yjs, Automerge): every character or node has a unique id, so replicas can merge updates in any order and still converge without a central authority. Great for offline and peer-to-peer, at the cost of extra metadata and tombstones.
    • Transport: a WebSocket to a sync server that relays updates and persists snapshots plus an operation log, and a separate ephemeral presence ("awareness") channel for cursors.
    • Offline: buffer local operations and sync them on reconnect.
    • Edge cases: permission changes mid-session, very large documents (compaction, lazy loading), per-user undo, announcing collaborators accessibly.
    What interviewers listen for
    • Apply edits locally first, then sync
    • OT transforms concurrent operations, usually via a central server
    • CRDTs merge in any order; good for offline
    • Separate ephemeral presence channel for cursors
    • Snapshots plus operation log; per-user undo

    Likely follow-up: How would you implement undo so it only reverts your own changes?

  23. 24.What is list virtualization (windowing), how does it work, and what are its trade-offs?mid

    Virtualization renders only the rows visible in the viewport plus a small overscan buffer, instead of thousands of DOM nodes. A scroll container holds a spacer with the full scrollable height; on scroll you compute the visible index range from scrollTop and row heights, then position those rows with transform or absolute offsets.

    • Fixed heights make the math trivial; variable heights need estimates corrected by measuring rendered rows (ResizeObserver).
    • Libraries: TanStack Virtual, react-window, Angular CDK virtual scroll.

    Trade-offs: browser find-in-page can't see unrendered rows; screen readers lose the list size (add aria-setsize and aria-posinset); focus is lost if a focused row unmounts; very fast scrolling can flash blank areas; scroll restoration and sticky elements get harder. For moderately long lists, CSS content-visibility: auto with contain-intrinsic-size is a cheaper win that keeps everything in the DOM.

    What interviewers listen for
    • Render only visible rows plus overscan
    • A spacer keeps the full scroll height
    • Variable heights need estimates and measurement
    • Trade-offs: find-in-page, accessibility, focus, blank flashes
    • content-visibility as a lighter alternative

    Likely follow-up: How would you virtualize a list whose row heights are unknown until images load?

  24. 25.Design a Kanban board like Trello, with drag-and-drop between columns.hard

    Requirements: columns and cards, drag to reorder within and across columns, reorder columns, a keyboard alternative, filters, and live updates from teammates.

    • Data model: normalized columns and cards by id; store order as a fractional index (a rank string between its neighbours) so a move updates only the moved card.
    • API: PATCH /cards/:id with { columnId, rank, version }; the server rejects stale versions.
    • Interaction: a Pointer Events based library (such as dnd-kit) rather than the HTML5 drag-and-drop API, which has limited touch support and no keyboard interaction; drop placeholders and auto-scroll near edges.
    • State: optimistic moves with rollback; remote moves arrive over a WebSocket and are queued while the user is mid-drag.
    • Performance: memoize cards so a drag doesn't re-render the whole board; virtualize very long columns.
    • Accessibility: keyboard dragging (Space to pick up, arrows to move, Space to drop, Escape to cancel) with live-region announcements, plus a "Move to…" menu.
    • Edge cases: two users moving the same card, WIP limits, dropping into collapsed or filtered columns.
    interface Card { id: string; columnId: string; rank: string; title: string; version: number }
    interface Column { id: string; title: string }
    interface BoardState {
      columnOrder: string[];
      columns: Record<string, Column>;
      cards: Record<string, Card>; // a column's cards = cards with its id, sorted by rank
    }
    What interviewers listen for
    • Normalized columns and cards with fractional ranks
    • Pointer-based DnD library over native HTML5 DnD
    • Optimistic moves with rollback and versioning
    • Keyboard dragging with live announcements
    • Handle concurrent moves from other users

    Likely follow-up: How does fractional indexing avoid rewriting every card position on a move?

  25. 26.What is hydration, why is it expensive, and what techniques reduce its cost?hard

    Hydration is when client-side JavaScript takes over server-rendered HTML: the framework re-runs components, rebuilds its internal tree against the existing DOM and attaches event listeners. The HTML appears fast, but the page isn't interactive until hydration finishes.

    Costs:

    • Downloading, parsing and executing JavaScript for the whole page, including static parts.
    • Long main-thread tasks that delay interactions and hurt INP; buttons look ready but don't respond.
    • Data sent twice: once as HTML and again as serialized state for the client.
    • Hydration mismatches when server and client output differ (dates, random values, browser-only APIs).

    Ways to reduce it: islands or partial hydration (hydrate only interactive components), lazy or progressive hydration (on visibility, idle or interaction), selective hydration with streaming and Suspense in React 18+, Server Components so static parts ship no JS, and resumability (Qwik), which avoids re-executing components on load.

    What interviewers listen for
    • Attaches listeners and rebuilds state over server HTML
    • Costs: JS download and execution, long tasks, INP
    • Data serialized twice; mismatch errors
    • Islands, lazy and selective hydration
    • Server Components and resumability ship less JS

    Likely follow-up: What causes a hydration mismatch, and how would you fix one caused by dates?

  26. 28.How do you choose between REST, GraphQL and a Backend-for-Frontend (BFF) when designing APIs for a frontend?mid
    • REST: resources at URLs with HTTP verbs. Simple, and GET responses are easy to cache in browsers and CDNs. Downsides: over-fetching, under-fetching and several round trips per screen, with endpoints drifting from what the UI needs.
    • GraphQL: one endpoint with a typed schema; the client asks for exactly the fields it needs, often in one request. Great for many clients with different needs and for generated TypeScript types. Costs: HTTP caching is harder (queries are usually POSTed; persisted queries make cacheable GETs practical), the server must limit expensive queries, and resolvers need batching to avoid N+1 lookups.
    • BFF: a thin server layer per client (web, mobile) that aggregates downstream services and shapes responses for its UI. The frontend team owns it, and it can keep OAuth tokens out of the browser.

    They combine: a BFF can expose REST or GraphQL. I decide based on client diversity, team ownership and caching needs.

    What interviewers listen for
    • REST: simple and cache-friendly, but over/under-fetching
    • GraphQL: client-shaped queries, typed schema, fewer round trips
    • GraphQL costs: caching, query cost limits, N+1
    • BFF aggregates services per client, owned by frontend
    • They combine; choose by clients and caching needs

    Likely follow-up: How would you cache GraphQL responses on the client?

  27. 29.Design a monitoring dashboard with many real-time charts, like Grafana or Datadog.hard

    Requirements: a configurable grid of widgets, live time series, time-range selection, drill-down, and thousands of points per chart without jank.

    • Data: one multiplexed WebSocket or SSE stream with per-widget subscriptions, or polling for slow metrics; changing the time range fetches history over REST, then resumes streaming.
    • Update pipeline: buffer incoming points and flush on a fixed cadence or per animation frame instead of re-rendering per message; keep a bounded ring buffer per series.
    • Rendering: SVG for small charts, Canvas for thousands of points, WebGL for very large datasets; downsample (for example LTTB) to roughly one point per pixel.
    • Workers: parse, aggregate and downsample in a Web Worker to keep the main thread free.
    • Isolation: each widget has its own loading, error and empty states (error boundaries); off-screen widgets pause via IntersectionObserver.
    • Efficiency: pause or slow updates when the tab is hidden (visibilitychange).
    • Accessibility: text summaries or data tables for charts, never color alone, a control to pause live updates.
    • Edge cases: reconnecting and backfilling gaps, clock skew and time zones, stale-data indicators, per-user saved layouts.
    What interviewers listen for
    • Multiplexed stream with per-widget subscriptions
    • Batch updates per frame; bounded ring buffers
    • Canvas or WebGL plus downsampling for dense data
    • Web Workers for parsing and aggregation
    • Pause hidden widgets; backfill gaps on reconnect

    Likely follow-up: How would you let users share a dashboard view with a specific time range?

  28. 30.What are micro-frontends? What are their pros and cons, and when would you use them?hard

    Micro-frontends split one web app into independently developed and deployed pieces, usually one per business domain and team, composed into a single experience.

    Composition options: build-time packages (simple, but you lose independent deploys), runtime integration such as Module Federation or Web Components, iframes (strong isolation, poor integration), or server/edge composition.

    • Pros: team autonomy, independent releases, smaller codebases per team, incremental migration off a legacy stack, fault isolation.
    • Cons: duplicated dependencies and bigger bundles unless shared libraries are carefully versioned (React must be a singleton), inconsistent UX without a shared design system, harder cross-app communication and routing, complex local development and end-to-end testing, more operational overhead.

    Use them when many teams are blocked on one deploy pipeline, or during a gradual migration. For a single team, a well-modularized monolith or monorepo is usually the better choice.

    What interviewers listen for
    • Independently deployed apps composed into one UI
    • Composition: build-time, Module Federation, iframes, edge
    • Pros: team autonomy, independent deploys, migration
    • Cons: duplicate deps, inconsistent UX, complexity
    • Use for many teams; otherwise a modular monolith

    Likely follow-up: How would micro-frontends share state such as the logged-in user?

  29. 31.How do you optimize images on a content-heavy website?easy

    Images are usually the heaviest bytes on a page and often the LCP element, so:

    • Right format: AVIF or WebP with a JPEG or PNG fallback via picture, SVG for icons and logos, and video instead of large animated GIFs.
    • Right size: responsive srcset with sizes, so each device downloads a suitably sized file; never ship a 2400px image into a 400px slot.
    • Right priority: loading="lazy" for below-the-fold images, but never for the LCP image, which should load eagerly with fetchpriority="high" (plus a preload if it is discovered late, such as a CSS background).
    • No layout shift: always set width and height (or aspect-ratio) so the browser reserves space.
    • Delivery: an image CDN that resizes, compresses and picks the format from the Accept header, with long-lived caching.
    • Perceived speed: blurred or dominant-color placeholders and decoding="async".
    What interviewers listen for
    • Modern formats (AVIF, WebP) with fallbacks
    • Responsive srcset and sizes
    • Lazy-load below the fold, never the LCP image
    • Set width and height to prevent CLS
    • Image CDN for resizing, format negotiation, caching

    Likely follow-up: How would you find and speed up the LCP image on a page?

  30. 32.What is a service worker, how does its lifecycle work, and what makes a web app a PWA?mid

    A service worker is a script that runs on its own thread, outside any page, and acts as a programmable network proxy for its scope. It requires HTTPS (localhost is allowed) and has no DOM access.

    • Lifecycle: register() → install (precache the app shell) → waiting (a new version waits until no tab uses the old one, unless it calls skipWaiting()) → activate (delete old caches; clients.claim() takes control of open pages) → handles fetch, push and sync events.
    • Caching strategies: cache-first for hashed assets, network-first for fresh content like articles, stale-while-revalidate for things like avatars, network-only for mutations. Workbox packages these.
    • PWA: a service worker plus a web app manifest (name, icons, start_url, display) served over HTTPS makes the app installable, offline-capable and able to receive push.

    Pitfall: a buggy service worker can keep serving a stale app, so version caches and prompt users to reload when an update is waiting.

    What interviewers listen for
    • Programmable network proxy on its own thread; HTTPS only
    • install, waiting, activate; skipWaiting and clients.claim
    • Cache-first, network-first, stale-while-revalidate strategies
    • PWA = service worker + manifest + HTTPS
    • Version caches and prompt for updates

    Likely follow-up: How would you tell users that a new version of the app is available?

  31. 33.Design the checkout flow for an e-commerce site.hard

    Requirements: cart review, shipping address and method, payment, order review and confirmation, guest checkout; non-functional: security, reliability (never double-charge) and conversion.

    • Flow: a multi-step form with the step in the URL so Back and refresh work; the draft lives on the server (a checkout session), not only in memory.
    • Validation: schema validation on the client for fast feedback, but the server is authoritative for prices, tax, stock and discounts; never trust client totals.
    • Payment: the provider's hosted fields or iframe (for example Stripe Elements) so card data never touches your servers, which shrinks PCI scope; support 3-D Secure challenges and wallets.
    • Placing the order: disable the button while pending and send an idempotency key so retries can't create duplicate charges; if the response is lost, poll the order status.
    • Performance: minimal third-party scripts, code-split steps, prefetch the next step.
    • Accessibility: labels and correct autocomplete values (shipping street-address, cc-number), an error summary that receives focus, announced step changes.
    • Edge cases: price or stock changes mid-checkout, expired sessions, declined cards, funnel analytics to spot drop-off.
    What interviewers listen for
    • Multi-step form with URL steps and a server-side draft
    • Server is authoritative for prices and stock
    • Hosted payment fields to reduce PCI scope
    • Idempotency key prevents double charges
    • autocomplete attributes and a focused error summary

    Likely follow-up: What happens if the network drops right after the user clicks "Place order"?

  32. 34.Why and how would you normalize data in a client-side cache or store?mid

    APIs often return nested data: a post embeds its author and comments, and the same user appears in dozens of places. Storing that as-is duplicates entities, so updating a user's avatar means finding every copy, and copies drift out of sync.

    Normalizing means storing each entity type in a lookup table keyed by id and referencing other entities by id:

    • users: { byId: { u1: {...} }, allIds: ['u1'] }
    • posts: { byId: { p1: { authorId: 'u1', commentIds: [...] } } }

    Benefits: one source of truth, O(1) lookups and updates, and narrower re-renders because components subscribe to a single entity. Tools: Redux Toolkit's createEntityAdapter, normalizr, and Apollo Client's cache, which normalizes automatically by __typename and id.

    Costs: you denormalize in memoized selectors and must clean up references on delete. For simple screens, a query cache keyed by request (TanStack Query) without normalization is often enough.

    What interviewers listen for
    • Nested API data duplicates entities
    • Store entities by id and reference by id
    • One source of truth, cheap updates
    • createEntityAdapter, normalizr, Apollo cache
    • Needs memoized selectors; not always necessary

    Likely follow-up: How does Apollo Client decide that two query results refer to the same object?

  33. 35.Design a design system and component library that many product teams will use.hard

    Goals: consistent UI, accessibility by default, faster delivery, and painless upgrades across many apps.

    • Design tokens: color, spacing, typography, radius and motion defined once (JSON) and transformed into CSS custom properties and platform outputs (for example with Style Dictionary); semantic tokens like --color-bg-danger enable theming, dark mode and multiple brands at runtime.
    • Components: primitives (Button, Input, Dialog) and composites built from them, with consistent, composable APIs (variant, size, controlled and uncontrolled modes, slots or compound components) that pass through native attributes and forward refs.
    • Accessibility: implement WAI-ARIA Authoring Practices patterns, focus management and keyboard support once, tested with axe and screen readers.
    • Output: zero-runtime CSS or CSS Modules, tree-shakeable ESM, typed props; Web Components if many frameworks must be supported.
    • Distribution: versioned packages with semver, changelogs (Changesets), deprecation warnings and codemods for breaking changes.
    • Docs and quality: Storybook with usage guidelines, visual regression and interaction tests in CI.
    • Governance: a contribution model, an RFC process, and adoption metrics showing which teams run which version.
    What interviewers listen for
    • Design tokens as the single source of truth
    • Composable, consistent component APIs
    • Accessibility built in and tested
    • Semver, changelogs and codemods for upgrades
    • Storybook docs, visual regression, governance

    Likely follow-up: How would you roll out a breaking change to a component used by 40 apps?

  34. 36.How would you implement authentication in a single-page app? Where do tokens live, and how do you handle expiry?hard
    • Session cookie: after login the server sets an HttpOnly; Secure; SameSite=Lax cookie that JavaScript can't read, so XSS can't steal it. Add CSRF protection for state-changing requests.
    • Third-party identity (OAuth 2.0 / OpenID Connect): use the Authorization Code flow with PKCE; the implicit flow is deprecated. Better still, run the flow in a BFF that keeps tokens server-side and gives the browser only a session cookie.
    • If the SPA must hold tokens: keep a short-lived access token in memory and a rotating refresh token in an HttpOnly cookie; avoid localStorage, which any injected script can read.
    • Expiry: on a 401, refresh once, queue concurrent requests while refreshing, then retry them; if the refresh fails, redirect to login and remember the intended URL.
    • Boundaries: route guards and hidden buttons are UX only; the server enforces authorization. Sync logout across tabs with BroadcastChannel and clear cached user data.
    What interviewers listen for
    • HttpOnly, Secure, SameSite session cookies
    • OAuth Authorization Code with PKCE, ideally via a BFF
    • In-memory access token, rotating refresh token
    • Refresh once on 401 and queue concurrent requests
    • Server enforces authorization; sync logout across tabs

    Likely follow-up: Why is the OAuth implicit flow no longer recommended?

  35. 37.Design a reusable modal dialog system for a large application.mid

    Requirements: open dialogs from anywhere (including non-UI code), stack them, support confirm dialogs that return a result, animate, and be fully accessible.

    • Primitive: the native dialog element with showModal() provides the top layer (no z-index wars), an inert background, Escape handling via the cancel event, and ::backdrop. Otherwise, a portal with manual focus trapping and the inert attribute on the rest of the page.
    • API: a declarative Dialog with open and onClose, plus an imperative manager for one-offs, e.g. const ok = await confirm({ title, message }), backed by a provider that keeps a stack.
    • Accessibility: label the dialog by its title with aria-labelledby (custom dialogs also need role="dialog" and aria-modal="true"); move focus inside on open, keep Tab inside, return focus to the trigger on close, close on Escape, and lock background scroll.
    • Performance: mount on open and lazy-load heavy content.
    • Edge cases: nested dialogs, closing on route change, deep-linkable dialogs via the URL, unsaved-changes prompts, mobile virtual keyboards, reduced motion.
    What interviewers listen for
    • Native dialog with showModal, or portal plus inert
    • Declarative component plus promise-based imperative API
    • Focus moved in, trapped, and returned on close
    • Escape closes; labelled by title; scroll locked
    • Handle stacking, route changes, deep links

    Likely follow-up: How would you make a modal deep-linkable so a URL opens it directly?

  36. 38.Design a toast notification system, like the "Message sent" pop-ups in many web apps.easy

    Requirements: show success, error and info messages from anywhere, auto-dismiss, stack several, and support an optional action such as Undo.

    • API: an imperative toast.success('Saved', { action, duration }) callable outside components, backed by a small store or event emitter holding a queue; a Toaster component renders it in a portal.
    • Behavior: cap visible toasts (say three) and queue the rest, dedupe identical messages, pause the auto-dismiss timer on hover and focus, and let errors stay until dismissed.
    • Accessibility: render live regions on page load (role="status" for polite messages, role="alert" sparingly for errors) and insert text into them, because regions added dynamically are often not announced. Don't move focus to toasts, make actions keyboard-reachable, give people enough time to read, and never make a disappearing toast the only way to reach an important action.
    • Styling: fixed position that respects safe areas, animations that honor reduced motion.
    • Edge cases: bursts of toasts, route changes, SSR (no window), long messages.
    What interviewers listen for
    • Imperative API backed by a queue store
    • Cap visible toasts, dedupe, pause timer on hover
    • Pre-rendered live regions: status polite, alert for errors
    • Never steal focus; actions keyboard-reachable
    • Important actions must not rely on vanishing toasts

    Likely follow-up: How would you implement an Undo action for a delete shown in a toast?

  37. 39.What kinds of race conditions happen in frontend UIs, and how do you prevent them?mid
    • Out-of-order responses: typing "rea" then "react" fires two requests, and the slower first one resolves last, overwriting newer results. Abort the previous request with AbortController, ignore responses that aren't the latest, or use switchMap in RxJS.
    • Stale effects: a component's props change or it unmounts before a fetch finishes; an effect cleanup aborts the request or sets an ignore flag.
    • Double submits: rapid clicks create duplicate orders. Disable the button while pending and send an idempotency key.
    • Lost updates: two tabs or users edit the same record. Use optimistic concurrency: send a version or an ETag in If-Match, and the server replies 412 Precondition Failed if the record changed.
    • Optimistic conflicts: a background refetch overwrites an in-flight optimistic change, so cancel queries before mutating.
    • Refresh stampede: many simultaneous 401s trigger many token refreshes; share one in-flight refresh promise.
    What interviewers listen for
    • Abort or ignore stale responses
    • Effect cleanup on prop change or unmount
    • Disabled submit plus idempotency keys
    • Versions or ETag with If-Match prevent lost updates
    • Share one in-flight token refresh

    Likely follow-up: How would you resolve a race between an optimistic update and a WebSocket event for the same item?

  38. 40.How do you make accessibility part of the engineering process across a large product with many teams?hard
    • Standard: agree on a target, typically WCAG 2.2 AA, and put it in the definition of done.
    • Foundations: semantic HTML first, and an accessible design system that implements WAI-ARIA Authoring Practices patterns (dialogs, menus, comboboxes, tabs) once, with focus management and keyboard support built in; color tokens that meet contrast minimums (4.5:1 for normal text, 3:1 for large text).
    • Automation: eslint-plugin-jsx-a11y in the editor and axe-core in component and end-to-end tests in CI, remembering that automated tools catch only part of the problems.
    • Manual testing: keyboard-only passes and screen reader testing (NVDA, JAWS, VoiceOver) on key flows, plus audits with disabled users.
    • Design reviews: focus states, reading order, error messages, target sizes, reduced motion.
    • Culture: training, an accessibility champion per team, and a11y issues tracked like any bug, with severity.

    In SPAs, also manage focus and announcements on route changes.

    What interviewers listen for
    • Target WCAG 2.2 AA in the definition of done
    • Accessible design system implements ARIA patterns once
    • Lint and axe in CI; automation is only partial
    • Manual keyboard and screen reader testing
    • Training, champions and tracked a11y bugs

    Likely follow-up: How do you manage focus and announcements on route changes in a single-page app?

  39. 41.Design the search results page for a large marketplace or content site.mid

    Requirements: results for a query with highlighted matches, filters and sorting, pagination, spelling suggestions, fast first results, and shareable URLs.

    • URL as state: /search?q=running+shoes&sort=relevance&page=2; the search box, filters and page read from and write to the URL, so Back, refresh and sharing work.
    • Rendering: SSR the first page of results for a fast LCP; internal search pages are usually kept out of the search engine index.
    • API: GET /search?q&filters&page returning { results, facets, total, correctedQuery }, with highlight ranges computed by the search engine.
    • Client behavior: debounce if searching as you type, abort stale requests, keep old results visible (dimmed) while new ones load, cache by query key.
    • Highlighting: apply the ranges by splitting text into safe spans, never by injecting HTML.
    • Analytics: log queries, zero-result searches and clicks with navigator.sendBeacon to improve ranking.
    • Accessibility: a search landmark (role="search" or the search element), the result count announced in a live region, one heading per result, sensible focus after submitting.
    • Edge cases: empty queries, no results (offer suggestions), typos, very long queries.
    What interviewers listen for
    • URL is the source of truth for query and filters
    • SSR the first results for a fast LCP
    • Abort stale requests; keep old results while loading
    • Safe highlighting from server-provided ranges
    • Search landmark and announced result count

    Likely follow-up: How would you measure whether search quality is improving?

  40. 42.What is a performance budget, and how would you set and enforce one for a team?mid

    A performance budget is a set of limits the team agrees not to exceed, turning "keep it fast" into a measurable, enforceable rule.

    • Quantity budgets: compressed JavaScript per route, total page weight, image bytes, number of requests or third-party scripts.
    • Timing budgets: LCP, INP and CLS at the 75th percentile, or lab metrics such as Total Blocking Time on a throttled mid-range phone.
    • Rule-based budgets: minimum Lighthouse scores.

    Set them from your real users' devices and networks, current baselines and competitors, and derive byte budgets from the timing goals. Enforce them in CI with tools like size-limit or bundler size warnings and Lighthouse CI assertions, failing or flagging pull requests that exceed them, and watch RUM dashboards with alerts for production regressions. When a feature needs more budget, make the trade-off explicit: remove something else or justify raising the limit.

    What interviewers listen for
    • Agreed limits make speed measurable and enforceable
    • Quantity, timing and rule-based budgets
    • Based on real devices, baselines and competitors
    • Enforced in CI with size-limit and Lighthouse CI
    • RUM alerts catch production regressions

    Likely follow-up: How would you handle a product request that blows the JavaScript budget?

  41. 43.Design a calendar and scheduling app like Google Calendar.hard

    Requirements: month, week and day views, creating and editing events by click or drag, recurring events, invitations and availability, time zones.

    • Data model: timed events stored as UTC instants plus an IANA time zone (Europe/Berlin), all-day events as plain dates, and recurrence as an RRULE (RFC 5545) with exceptions; occurrences are expanded only for the visible range.
    • API: GET /events?start=&end= for the visible range, prefetching adjacent ranges; mutations are optimistic.
    • Layout: a CSS grid for the time axis; an overlap algorithm groups colliding events into columns; all-day and multi-day events get their own row.
    • Interaction: drag to create, move and resize with snapping (say 15 minutes) using pointer events, with keyboard alternatives and an edit dialog.
    • Time: render in the user's zone with Intl.DateTimeFormat; handle DST days with 23 or 25 hours and locale settings such as week start and 12/24-hour clocks.
    • Accessibility: an agenda list view as an alternative, event labels with full date, time and title, keyboard navigation between days.
    • Edge cases: editing "this and following" occurrences, conflicts, events that cross time zones, offline edits.
    What interviewers listen for
    • UTC instants plus an IANA time zone per event
    • RRULE recurrence expanded for the visible range
    • Overlap layout algorithm for colliding events
    • Drag to create and resize with snapping
    • DST, locale week start, accessible agenda view

    Likely follow-up: How would you implement editing "this and all following" occurrences of a recurring event?

  42. 44.How would you add internationalization (i18n) and localization to a large web app?mid
    • Externalize strings: message ids with translations per locale, never string concatenation, because word order differs between languages.
    • ICU MessageFormat: handles plurals, gender and select, e.g. {count, plural, one {# file} other {# files}}; languages have different plural categories (Arabic has six).
    • Formatting: the Intl APIs (DateTimeFormat, NumberFormat, PluralRules, RelativeTimeFormat, Collator) for dates, currencies, numbers and sorting.
    • Locale selection: the user's saved preference first, then Accept-Language; put the locale in the URL (/de/...) for SEO, with hreflang links.
    • Layout: set lang and dir="rtl", use CSS logical properties (margin-inline-start), mirror directional icons, and leave room for longer text.
    • Performance: lazy-load only the active locale's messages, split per route.
    • Workflow: string extraction, a translation management system, and pseudo-localization in CI to catch hard-coded strings and truncation.

    Common libraries: FormatJS (react-intl), i18next, Angular's built-in i18n.

    What interviewers listen for
    • Externalize strings; never concatenate
    • ICU MessageFormat for plurals and gender
    • Intl APIs for dates, numbers and currency
    • Locale in URL with hreflang; RTL via logical properties
    • Lazy-load locale bundles; pseudo-localization in CI

    Likely follow-up: How would you test that a layout survives long German strings and right-to-left languages?

  43. 45.How would you handle errors and set up monitoring and observability for a production frontend?mid
    • Contain failures: error boundaries around routes and independent widgets, with a useful fallback and retry. In React they catch errors during rendering, not in event handlers or async code, so handle those with try/catch and error states in the data layer.
    • Catch the rest: global error and unhandledrejection listeners.
    • Error tracking: report to a tool like Sentry with the release version, route, browser and breadcrumbs; upload source maps privately so stack traces are readable; scrub PII; alert on new or spiking issues.
    • RUM: collect Core Web Vitals with the web-vitals library and custom timings with PerformanceObserver, sent with navigator.sendBeacon when the page is hidden; sample to control cost.
    • Tracing and logs: structured client logs and a traceparent header on API calls so frontend events link to backend traces.
    • Resilience: timeouts, retries with exponential backoff for idempotent requests, clear offline and error UI.

    Segment dashboards by release to catch bad deploys fast.

    What interviewers listen for
    • Error boundaries plus global error listeners
    • Error tracking with releases and private source maps
    • RUM for Web Vitals sent with sendBeacon
    • Trace headers link frontend and backend
    • Per-release alerts enable fast rollback

    Likely follow-up: What would you capture in breadcrumbs to make a bug reproducible?

  44. 46.Design an offline-first notes app that syncs across devices.hard

    Requirements: create, edit and search notes with no connection, sync across devices when back online, and never lose edits.

    • App shell: a service worker precaches HTML, JS and CSS so the app loads offline; updates are offered, not forced mid-edit.
    • Local database: IndexedDB (for example via Dexie) is the source of truth; the UI reads and writes locally, so every action is instant. Request navigator.storage.persist() to reduce the risk of eviction.
    • Sync engine: each change goes into an outbox with the note id and base version; when online (the online event, or Background Sync where supported), push the outbox, then pull changes since the last server cursor.
    • Conflicts: server-assigned versions detect them; resolve with last-writer-wins per field, keep both copies for the user to merge, or use a CRDT (Yjs, Automerge) for note bodies. Deletions become tombstones so they sync too.
    • Multi-tab: coordinate with BroadcastChannel and the Web Locks API so only one tab syncs.
    • UX: a visible sync status and conflict notices.
    • Edge cases: IndexedDB schema migrations, quota errors, clock skew (don't order by device clocks), very long offline periods.
    interface Note {
      id: string; // UUID generated on the device
      title: string;
      body: string;
      updatedAt: number; // for display only, not for ordering
      version: number; // last version confirmed by the server
      deleted: boolean; // tombstone so deletes sync
    }
    interface OutboxEntry { noteId: string; baseVersion: number; patch: Partial<Note> }
    What interviewers listen for
    • Service worker precaches the app shell
    • IndexedDB is the local source of truth
    • Outbox of changes: push, then pull since cursor
    • Versions detect conflicts; LWW, merge UI or CRDT
    • Tombstones, multi-tab locks, schema migrations

    Likely follow-up: How would you resolve two devices editing the same note while offline?

  45. 47.Design an embeddable poll widget that third-party sites can drop into their pages.easy

    Requirements: show a question and options, let each user vote once, show live results with percentages, and embed on any site without breaking it.

    • Embedding: an iframe gives style and security isolation (neither the host nor the widget can break the other); a Web Component with Shadow DOM is lighter but shares the page's JavaScript context. Keep the bundle tiny and reserve explicit dimensions to avoid layout shift, resizing via postMessage.
    • API: GET /polls/:id and POST /polls/:id/votes with { optionId }.
    • One vote per user: enforced on the server (by account, or a token plus rate limiting for anonymous users); a localStorage flag only remembers the vote for UX.
    • Results: optimistic update after voting, then live counts via polling or SSE; show text percentages, not just bar widths.
    • Accessibility: options as a radio group in a fieldset with a legend, a real submit button, results announced after voting.
    • Edge cases: closed polls, zero votes (avoid dividing by zero), rounded percentages summing to 99 or 101, third-party cookie blocking inside iframes, network errors.
    What interviewers listen for
    • Iframe or Web Component embedding with isolation
    • Tiny bundle and reserved size to avoid CLS
    • Server enforces one vote; localStorage is UX only
    • Optimistic result, then live counts
    • Radio group in a fieldset; text percentages

    Likely follow-up: How would you stop a script from casting thousands of fake votes?

  46. 48.Compare cookies, localStorage, sessionStorage, IndexedDB and the Cache API. When would you use each?easy
    • Cookies: small (about 4 KB each) and sent with every matching request, so reserve them for server-readable data like session ids, with HttpOnly, Secure and SameSite.
    • localStorage: a synchronous string key-value store that persists per origin, limited to a few megabytes. Fine for small preferences like theme; avoid large data (it blocks the main thread) and secrets (any script can read it).
    • sessionStorage: the same API, scoped to one tab and cleared when the tab closes; good for per-tab drafts or wizard state.
    • IndexedDB: an asynchronous, transactional database for structured data and blobs, with indexes, access from workers and much larger quotas. Use it for offline data, big caches and outboxes, usually via a wrapper like Dexie or idb.
    • Cache API: stores Request/Response pairs, mainly from a service worker for offline assets and API responses.

    All are scoped per origin and can be evicted under storage pressure unless navigator.storage.persist() is granted.

    What interviewers listen for
    • Cookies: tiny, sent with requests; for session ids
    • localStorage: sync, small, persistent, readable by any script
    • sessionStorage: per tab, cleared on close
    • IndexedDB: async, large, structured, works in workers
    • Cache API: request/response pairs for service workers

    Likely follow-up: Why is localStorage a poor place for an authentication token?

  47. 49.Design a file explorer tree view like the sidebar in VS Code or Google Drive.mid

    Requirements: expand and collapse folders, lazy-load children, select, rename, create, delete, drag to move, search, and full keyboard support for trees with thousands of nodes.

    • Data model: normalized nodes { id, name, type, parentId, childIds, hasChildren } so any node can be updated or moved cheaply; UI state separately holds expanded ids, the selected id, and per-node loading and error.
    • API: GET /nodes/:id/children on first expand (then cached) and PATCH /nodes/:id for rename or move, with optimistic updates and rollback.
    • Rendering: flatten the visible nodes (respecting expansion) into an array with depth, then virtualize it and indent by depth.
    • Accessibility: the ARIA tree pattern (role="tree", treeitem, group, aria-expanded, aria-level, aria-selected) with a roving tabindex. Up/Down move, Right expands or enters a folder, Left collapses or goes to the parent, plus Home/End and type-ahead.
    • Sorting: folders first, then locale-aware names via Intl.Collator.
    • Edge cases: name collisions, moving a folder into its own descendant (block it), very deep paths, empty folders, concurrent changes from other clients.
    What interviewers listen for
    • Normalized nodes with parentId and childIds
    • Lazy-load children on first expand
    • Flatten visible nodes, then virtualize
    • ARIA tree pattern with roving tabindex
    • Block moving a folder into its own descendant

    Likely follow-up: How would you implement search that reveals matches inside collapsed folders?

  48. 50.How should web fonts be loaded so they do not hurt performance or cause layout shifts?mid
    • Fewer, smaller files: WOFF2 only, subset to the characters you need (split with unicode-range), and consider one variable font instead of many weights, or simply a system font stack.
    • Self-host: browsers now partition their HTTP cache by site, so a shared font CDN no longer gives a cross-site cache benefit, and self-hosting avoids an extra connection.
    • Discover early: link rel="preload" as="font" type="font/woff2" crossorigin for the one or two critical fonts; crossorigin is required even for same-origin fonts.
    • font-display: swap shows fallback text immediately and swaps when the font arrives (good for brand fonts, but it can shift layout); optional gives the font a very short window and otherwise keeps the fallback, avoiding layout shift; block hides text and is rarely appropriate.
    • Reduce CLS: tune the fallback with size-adjust, ascent-override and descent-override so it matches the web font's metrics.
    • Cache: long-lived Cache-Control on versioned font files.
    What interviewers listen for
    • WOFF2, subsetting and variable fonts
    • Self-host; preload critical fonts with crossorigin
    • font-display: swap vs optional trade-off
    • Metric-matched fallbacks with size-adjust reduce CLS
    • Long-lived caching of versioned font files

    Likely follow-up: When would you choose font-display: optional over swap?

  49. 51.How would you design feature flags and A/B testing for a frontend?hard

    Feature flags decouple deploying code from releasing it.

    • Flag types: release flags (gradual rollouts, dark launches), experiment flags (A/B tests), ops flags (kill switches) and permission flags. Each gets an owner and an expiry date.
    • Evaluation: evaluate on the server or at the edge and bootstrap the results into the HTML, so the first render is already correct; evaluating in the browser after load causes a flash of the wrong variant and layout shift.
    • Consistency: bucket users deterministically by hashing a stable id with the experiment key, so they see the same variant across sessions and devices.
    • Experiments: log an exposure event when the variant is actually shown, define primary and guardrail metrics up front, run until the planned sample size instead of stopping at the first significant result, and check for sample ratio mismatch.
    • Hygiene: safe defaults when the flag service is down, typed flag names, and prompt removal of stale flags.
    What interviewers listen for
    • Decouple deploy from release
    • Evaluate on the server or edge to avoid flicker
    • Deterministic hashing for sticky bucketing
    • Log exposures; predefine metrics and sample size
    • Safe defaults and cleanup of stale flags

    Likely follow-up: How would you run an experiment on a statically generated page?

  50. 52.What testing strategy would you use for a large frontend application?easy

    I follow the test pyramid, adjusted for UI work:

    • Static analysis: TypeScript and ESLint are the cheapest layer and catch whole classes of bugs before any test runs.
    • Unit tests: many fast tests for pure logic such as utilities, reducers, formatters and hooks (Vitest or Jest).
    • Component and integration tests: render components with Testing Library and assert on what users see and do (roles, labels, text) rather than implementation details; mock the network with MSW. This layer usually gives the best confidence per cost.
    • End-to-end tests: a small set of critical journeys (sign-up, checkout) in real browsers with Playwright or Cypress, run against preview deployments.
    • Specialized checks: visual regression for the design system, axe for accessibility, Lighthouse CI for performance, and contract tests or generated types to keep the API in sync.

    Everything runs in CI on every pull request, and flaky tests are fixed or quarantined quickly, because a suite nobody trusts gets ignored.

    What interviewers listen for
    • Static analysis with TypeScript and ESLint
    • Many unit tests for pure logic
    • Component tests on user-visible behavior, network mocked with MSW
    • A few end-to-end tests for critical journeys
    • Visual, accessibility and performance checks in CI

    Likely follow-up: How do you keep end-to-end tests from becoming flaky?

  51. 53.Design a rich text editor like the ones in Notion or Medium, at a high level.hard

    Requirements: formatting (bold, headings, lists, links), images and embeds, mentions, Markdown shortcuts, undo/redo, pasting from other apps, and optional collaboration.

    • Why not raw contenteditable: browsers produce inconsistent HTML, so modern editors (ProseMirror, Lexical, Slate) keep their own document model: a schema of nodes and marks, immutable state, and transactions. The model renders to the DOM, and user input (beforeinput events) is translated back into transactions.
    • Storage: persist the JSON document, not HTML; render HTML on the server through a sanitizer.
    • Paste: parse pasted HTML, normalize it to the schema and drop everything else, which also blocks XSS.
    • Features as plugins: toolbar commands, input rules (typing ## makes a heading), mentions with an autocomplete popup, and a history plugin that groups steps for undo.
    • Collaboration: bind the model to OT or a CRDT (for example y-prosemirror).
    • Accessibility: a role="toolbar" with aria-pressed toggle buttons, keyboard shortcuts for every action, a labelled editing region.
    • Edge cases: IME composition, spellcheck, very large documents, mobile selection handles.
    What interviewers listen for
    • Own document model instead of raw contenteditable
    • Schema, immutable state and transactions
    • Store JSON; sanitize and normalize pasted HTML
    • Plugins for input rules, mentions and history
    • Accessible toolbar, IME handling, collaboration support

    Likely follow-up: How would you implement @mentions inside the editor?

  52. 54.How would you set up CI/CD and deployments for a frontend application?mid
    • CI on every pull request: install from the lockfile with dependency caching, then lint, type-check, unit and component tests, build, a bundle-size check, and end-to-end tests against a preview deployment that reviewers can open.
    • Build once, promote: the same artifact moves from staging to production, with environment config injected at runtime rather than baked into separate builds where possible.
    • Deploy order: upload content-hashed assets to the CDN first (long-lived, immutable caching), then switch the HTML (short or no caching) so it never references missing files.
    • Version skew: keep previous assets available for a while, because open tabs will lazy-load old chunks; on a chunk load error, reload the page.
    • Safe releases: atomic deploys with instant rollback, canary or percentage rollouts, and feature flags so risky features ship dark.
    • After deploy: upload source maps to error tracking, tag the release, and watch error rates and Web Vitals per release.
    What interviewers listen for
    • PR pipeline: lint, types, tests, build, size, E2E
    • Preview deployments for every pull request
    • Upload hashed assets before switching HTML
    • Keep old chunks; handle chunk load errors
    • Rollback, canaries, flags and release monitoring

    Likely follow-up: How would you roll back a bad frontend deploy within a minute?

  53. 55.What are the benefits and costs of a monorepo for frontend teams, and how do you keep one fast?hard

    A monorepo holds many apps and packages (web app, design system, shared utilities) in one repository. It is not a monolith: each package can still build and deploy independently.

    • Benefits: atomic changes across packages in one pull request, shared tooling and configs (TypeScript, ESLint), code sharing without publishing, a single dependency version policy, and consistent CI.
    • Costs: CI time grows with the repo, tooling is more complex, ownership boundaries blur, and one bad shared change can break many apps.
    • Keeping it fast: workspaces (pnpm, npm or Yarn) to link packages; a task runner such as Turborepo or Nx that builds a dependency graph and runs only affected projects, with local and remote caching; enforced module boundaries so apps import only public entry points; and CODEOWNERS for review ownership.

    Choose it when teams share a lot of code and want to evolve it together; separate repos suit truly independent products.

    What interviewers listen for
    • One repo, many independently deployable packages
    • Atomic cross-package changes and shared tooling
    • Costs: CI scale, tooling complexity, blast radius
    • Affected-only builds with remote caching (Turborepo, Nx)
    • Enforced module boundaries and CODEOWNERS

    Likely follow-up: How do monorepos relate to micro-frontends?

  54. 56.Design a maps or location-based UI, such as a store locator or a ride-hailing map, at a high level.hard

    Requirements: pan and zoom a map, show many markers, search places, show the user's location, and keep a results list in sync with the map.

    • Map rendering: maps are drawn from tiles, either raster images or vector tiles rendered with WebGL (MapLibre GL, Mapbox, Google Maps). Choose a provider by cost, quotas, styling and offline needs. Lazy-load the heavy map library and show a static preview first.
    • Data: fetch points for the visible bounding box, debounced on the map's move-end event and cached by area; encode center and zoom in the URL.
    • Many markers: cluster them (for example with supercluster) and draw them in a WebGL layer rather than as thousands of DOM nodes.
    • Location: navigator.geolocation requires a secure context and user permission, so ask only after a user action and fall back to manual search if denied.
    • Real time (ride-hailing): stream positions over a WebSocket and interpolate movement between updates.
    • Accessibility: an equivalent list view of results, keyboard pan and zoom controls, labelled markers.
    • Edge cases: poor GPS accuracy, going offline, API quota limits, very dense areas.
    What interviewers listen for
    • Tile-based rendering; vector tiles via WebGL
    • Fetch by visible bounds, debounced on move end
    • Cluster markers and render them in a WebGL layer
    • Geolocation permission only after a user action
    • Accessible list view alongside the map

    Likely follow-up: How would you smoothly animate a moving car from sparse location updates?

esc