The goal is not to memorize trivia. The goal is to repeatedly turn an ambiguous prompt into a working,
accessible, resilient UI while explaining clean state, effects, data loading,
performance, and tradeoffs.
Modern web-engineering rounds tend to emphasize TypeScript, Node.js, React, CSS, UI/UX quality,
performance, security, offline functionality, high-concurrency systems, low latency, and real-time
experiences. This guide optimizes for the practical version of that: chat-style UIs, screenshot-to-UI work,
async handling, accessibility, performance, and concise explanations.
Lead withData shape, state shape, edge states, then first working slice.
Win byExplaining why a value is state, derived, ref, or effect-owned.
AvoidSilent coding, duplicated state, custom div controls, and async without cleanup.
Close withTests, accessibility, performance measurement, and failure modes.
Likely live-coding signals
Can you turn vague product requirements into clear component state?
Can you build the UI in small working slices instead of stalling on architecture?
Can you handle loading, empty, error, optimistic, and disabled states?
Can you prevent stale async results from overwriting newer UI?
Can you write semantic HTML and keyboard-friendly interactions?
Can you talk through performance without overusing memoization?
The shape of a strong answer
Restate the product goal in one sentence.
Name the data model and minimum state.
Implement the first working version quickly.
Add edge states and accessibility affordances.
Harden async, keyboard, and mobile behavior.
End with tradeoffs and next tests.
Interview north star
In a practical web round, saying "I am keeping only the user's inputs and
selected IDs in state; everything else is derived in render" is often more valuable than introducing a
bigger architecture too early.
02 / framework
C-S-A-R-E: The Repeatable Build Framework
Use this on every prompt. It keeps you from jumping straight into JSX before you know what the UI must remember,
what it can derive, and what external systems it must synchronize with.
core rule
Clarify what can change, store only the source of truth, derive everything else.
ClarifyAsk the one question that changes code: local vs server, single vs multi-select, realtime vs snapshot.
StateWrite state names before JSX so your implementation has a spine.
ActionsName handlers by user intent, not DOM events: submit, retry, choose, dismiss.
EffectsUse only for external sync: fetch, subscription, timer, focus, scroll, storage.
Clarify
Ask only questions that change implementation. "Should search be local or server-backed?" is useful. "What color?" usually is not.
What data shape do I receive?
What user actions change state?
What edge states are required?
Keyboard and mobile expectations?
State
Write the minimum source of truth before rendering. Avoid storing filtered lists, counts, or labels that can be calculated.
Inputs: query, draft message, selected tab.
Server: status, data, error.
UI: open menu ID, focused index.
Refs: DOM nodes, latest request ID.
Actions
Name handlers by user intent. Keep state transitions close to the action that caused them.
onQueryChange
onSubmitMessage
onSelectResult
onRetry
Render
Derive UI from state in the render path. Show the state machine clearly: idle, loading, empty, error, success.
Effects
Use effects only to synchronize with something outside React: network, browser APIs, timers, focus, scroll, subscriptions, storage.
C-S-A-R-E scratchpad
/*
Clarify:
- Is data local or server-backed?
- What are loading, empty, error, and disabled states?
- What keyboard/mobile behavior matters?
State:
- query: string
- selectedId: string | null
- status: "idle" | "loading" | "success" | "error"
- data: Item[]
- error: string | null
Actions:
- changeQuery(next)
- selectItem(id)
- retry()
- clear()
Render:
- visibleItems = filter/sort data during render
- status controls loading/empty/error/success
Effects:
- fetch when query changes
- abort stale fetches
- focus/scroll sync when needed
*/
03 / react 0 to hero
React Concepts You Should Be Able To Build With
These are the concepts that show up in product-style screens. Learn them through implementation, not definition
recitation.
Default moveUse controlled state, render from it, then add edge states.
State testIf it can be computed from props or state, derive it during render.
Effect testIf nothing outside React is involved, you probably do not need an effect.
TypeScriptType props, event handlers, reducer actions, and async state unions.
Components, props, and render thinking
Components are functions that turn props and state into UI. A good interview component has a small public
surface: clear props, predictable callbacks, and no hidden dependency on global state.
High-yield answer
"I keep components small by making their inputs explicit: data in through props, user intent out through callbacks."
Trap: mixing fetching, selection, formatting, and layout in one component too early.
Upgrade: split only after the first slice works or the prop boundary becomes obvious.
Store values that the user or server can change independently. Do not store
values that can be calculated from existing state during render. This reduces bugs where two pieces
of state drift apart.
High-yield answer
"State is the minimum set of facts that can change independently. Filtered lists, counts, labels, and selected objects are derived."
Trap: useEffect plus setFilteredItems for local search.
Upgrade: if filtering is expensive, memoize after measuring; do not duplicate source of truth.
Put event-specific logic in event handlers. If a user clicked Submit, your handler has the context. Avoid
"watching" state with an effect to infer what happened.
High-yield answer
"Submit belongs in the submit handler because the event is the source of truth for that action."
Trap: toggling isSubmitted and using an effect to fire the request.
Upgrade: disable the button while saving, preserve user input on failure, and announce errors.
Effects are for synchronization with external systems. Common interview uses: fetch data, subscribe to events,
manage timers, sync focus/scroll, and attach browser listeners with cleanup.
High-yield answer
"This effect synchronizes React with an external system, and cleanup reverses the subscription or cancels stale work."
Trap: using effects to calculate render data or mirror props into state.
Upgrade: call out dependencies, cleanup, and stale closure behavior while coding.
Good effect: fetch when query changes and cancel stale work.
Good effect: scroll a message list when a new message appears.
Good effect: install a keydown listener and remove it on cleanup.
Suspicious effect: copy props.items into visibleItems state.
Suspicious effect: submit an API call because isSubmitted became true.
When to use useReducer
Start with useState. Reach for useReducer when many related transitions update the
same state object and action names make the code easier to reason about. Do not use it to look advanced.
High-yield answer
"I would keep useState until transitions start coupling together; then a reducer makes allowed state changes explicit."
Trap: reducer ceremony for a single boolean or one input.
Upgrade: model async state as a union so impossible combinations are harder to represent.
Async fetch with AbortController and stale-result guard
Use cancellation to stop work when possible, and a latest-request guard to prevent
older responses from overwriting newer state if responses resolve out of order.
High-yield answer
"The abort prevents wasted work after unmount or dependency change; the request ID prevents out-of-order responses from winning."
Trap: only using AbortController and assuming it solves every race.
Upgrade: preserve prior data during refresh when that feels better than flashing empty UI.
Practical frontend rounds reward people who know the browser as a platform: HTML, CSS, JS, network, performance,
accessibility, security, and realtime constraints.
web rule
Explain what the browser is doing, not just what React is doing.
XSS: do not inject raw HTML; sanitize user content if unavoidable.
CSRF: same-site cookies plus CSRF tokens for state-changing cookie-authenticated requests.
Auth storage: know tradeoffs between HTTP-only cookies and JS-accessible tokens.
Secrets: never put private API keys in client code.
CSP: defense-in-depth to restrict script, style, image, and connection sources.
Rate limits: protect expensive endpoints and realtime systems from abuse.
Realtime and offline
High-yield answer
"I choose the simplest transport that matches directionality and latency: polling, SSE, or WebSocket, then design reconnect and backpressure."
Trap: rendering every streamed event one by one and making the UI janky.
Upgrade: batch stream updates, preserve drafts offline, queue mutations, and reconcile after reconnect.
Polling: simplest, but inefficient for low-latency updates.
SSE: server-to-client streaming over HTTP, good for feeds and AI token streams.
WebSockets: bidirectional realtime channel, good for chat, presence, collaborative state, and live dashboards.
Offline: queue mutations, persist drafts, show sync state, reconcile on reconnect.
Backpressure: do not render every event if the stream is faster than the UI can update.
06 / frontend system design
System Design For Web Engineers
For frontend-heavy system design, you are designing the user experience, data flow, client architecture,
reliability model, performance profile, and operating plan. The backend matters, but keep tying choices back
to user-visible behavior.
StartUsers, devices, core flows, scale, latency, and non-goals.
API: cursor-based feed, mutation endpoints, media upload flow, ranking metadata hidden from client.
Optimistic updates: likes and reposts update immediately with rollback on failure.
Cache: separate identity cache for users/posts from feed order cache.
Realtime: show "new posts" affordance rather than shifting content while reading.
Perf: virtualized feed, image lazy-loading, prefetch next cursor near viewport end.
A11y: heading landmarks, actionable buttons with labels, focus retention after mutations.
System design answer skeleton
1. Clarify product goal, users, devices, and scale.
2. List core flows and non-goals for v1.
3. Define data model and stable IDs.
4. Sketch APIs: reads, writes, pagination, streaming, errors.
5. Split client state:
- server cache
- local UI state
- form/draft state
- optimistic mutation state
6. Cover edge states: loading, empty, error, offline, reconnecting.
7. Discuss performance: initial load, list virtualization, batching, cache.
8. Discuss accessibility and security.
9. Name observability metrics and rollout plan.
10. Close with tradeoffs and future work.
07 / practice problems
Practical Drills
Work down the list like lab exercises. Timebox one, ship the smallest working version, then use the twist as
the interviewer follow-up.
PassA working slice with correct state and no obvious async bug.
StrongEdge states, keyboard path, disabled states, and clear explanation.
ExcellentHandles follow-up twist without rewriting the architecture.
ReviewAfter each drill, write the state model and one bug you prevented.
30 minReactA11y
Tabs With Keyboard Support
Build tabs where clicking and arrow keys switch panels. Include selected state, focus styling, and semantic roles.
High-yield focus
State is selected tab ID. Derive active panel and ARIA state from it.
Acceptance criteria
Only active panel is visible.
ArrowLeft and ArrowRight move active tab.
Tabs are buttons with clear labels.
State is just selected tab ID.
Twist
Tabs come from API data and one tab may be disabled.
35 minReactState
Toast Queue
Build a toast system with add, auto-dismiss, manual dismiss, and stacked rendering.
High-yield focus
Each toast needs a stable ID and timer cleanup; array updates stay immutable.
Acceptance criteria
Each toast has stable ID.
Dismiss removes one toast.
Timers clean up on unmount.
No array mutation.
Twist
Pause auto-dismiss while hovering the toast region.
45 minReactA11y
Modal Confirm Dialog
Build a destructive confirm dialog with open, cancel, confirm, Escape close, focus restore, and disabled saving state.
Acceptance criteria
Dialog title and body are announced.
Escape closes unless saving.
Focus returns to opener.
Confirm button shows pending state.
Twist
Require typing a resource name before confirm is enabled.
45 minAsyncReact
Autocomplete Search
Build debounced search against an API with loading, empty, error, cancellation, highlighted matches, and keyboard selection.
High-yield focus
Keep raw input instant, debounce fetch query, abort stale requests, and guard response ordering.
Acceptance criteria
Debounce input by 200-300ms.
Abort stale fetches.
Older responses cannot overwrite newer results.
Enter selects active result.
Twist
Add recent searches from localStorage and merge them above server results.
40 minReactForms
Stepper Form
Build a three-step form with validation, back/next, review screen, and submit state.
Acceptance criteria
Current step is source of truth.
Cannot advance with invalid fields.
Back preserves draft values.
Review derives from form state.
Twist
Allow saving a draft to localStorage and restoring after refresh.
50 minReactData UI
Sortable Filterable Table
Build a table with query filtering, sortable columns, status filters, row selection, and empty states.
Week 3: Performance, testing, routing, data caching, offline basics. Build a small dashboard.
Week 4: Frontend system design. Run 8 mocks: chat, feed, autocomplete, dashboard, upload, notifications, docs editor, analytics.
Self-score after every mock
Area
0
1
2
Clarified scope
Started coding blind
Asked broad questions
Asked implementation-changing questions
State model
Duplicated derived state
Mostly clear
Minimal source of truth
Working slice
Incomplete
Happy path works
Happy path plus edge states
Async
Race bugs likely
Basic loading/error
Cancellation and stale guard
A11y
Div soup
Some labels/buttons
Keyboard, labels, status, focus
Explanation
Silent or scattered
Some tradeoffs
Clear reasoning while coding
09 / out-loud scripts
Short Explanations To Practice
These are not meant to sound rehearsed. They are meant to make your real thinking fast and legible while you code.
Before code"I will name state first, then ship the happy path."
During code"This is derived, so I am not storing it."
Async"Abort handles cleanup; request ID handles response ordering."
End"Next I would test keyboard, async races, mobile, and error states."
Starting"I will first clarify data shape and edge states, then build the happy path, then harden async and accessibility."
State"The source of truth is the query, selected ID, and server result status. The filtered list and selected item are derived during render."
Effect"This effect synchronizes with the network. It cleans up with AbortController and ignores stale responses so older requests cannot overwrite newer UI."
Forms"Submit logic belongs in the submit handler because that is where I know the user action happened."
A11y"I am using real buttons and labels so keyboard and assistive-tech users get the expected behavior for free."
Perf"I would measure before memoizing. The first wins are reducing unnecessary state, avoiding huge renders, and virtualizing long lists."
Design"For v1 I would keep transport simple, define stable IDs, make retries visible, and add observability around latency and failure rates."
Close"Given more time, I would add tests around keyboard behavior, async races, empty/error states, and mobile layout."
Common traps
Using useEffect to derive filtered data.
Forgetting response.ok with fetch.
Letting stale requests overwrite current results.
Using array index as key for reorderable lists.
Building custom clickable divs instead of buttons.
Ignoring loading, empty, and error states until the end.
Memoizing everything without proving cost.
Fast debug checklist
Can I reproduce the bug with a tiny sequence of clicks?
Which state value is wrong?
Is it source state or derived state?
Is a closure using old state?
Did an effect run more often than expected?
Is cleanup missing?
Is the key preserving or resetting state correctly?
10 / source links
Official References Worth Reviewing
This guide is written as practical prep, with links to official docs for deeper review. Use these references
when you want the primary-source version of a concept after practicing the implementation.
React: Thinking in React
Official guide for breaking UI into components, identifying state, and building from a data model.
This is the cram sheet. If you only review one section before the round, review this: the moves that most
often separate a decent frontend answer from a strong practical engineering answer.
Say first"I will clarify data shape, pick minimum state, ship the happy path, then harden."
Defend firstAbort stale fetches, guard response ordering, prevent duplicate submits.
Close firstTests for keyboard, async races, edge states, mobile layout, and failure paths.
Minimum state is the whole game
Before JSX, name the source of truth: user inputs, selected IDs, server status/data/error, and small UI state. Derived values stay out of state.
Effects are only for external synchronization
Fetch, timers, subscriptions, focus, scroll, storage, browser listeners. If it is just calculating what to show, do it during render.
Async needs both cleanup and ordering
AbortController stops old work when possible. A latest request ID stops older responses from overwriting newer UI.
HTML quality is not polish
Use buttons for actions, anchors for navigation, labels for inputs, headings in order, visible focus, and status/alert regions for async updates.
Performance answer starts with measurement
Say which cost you are targeting: network, JS bytes, render count, layout shift, main-thread blocking, list size, or image/font loading.
System design must stay user-visible
Every API/cache/realtime choice should map back to latency, reliability, offline behavior, ordering, accessibility, and observable failures.
The 90-second opening script
Memorize this shape
"I will restate the goal, clarify the data and edge states, write the minimum state model, build the happy path, then harden async, a11y, and performance."
Goal: "We need a UI that lets users search/select/send/filter/etc."
Data: "I need stable IDs, labels, status, timestamps, and any disabled/error fields."
State: "I will store only query, selected ID, status/data/error, draft/open/focused index."
Render: "Visible rows, selected item, counts, and disabled labels are derived."
Hardening: "Then I will add loading, empty, error, keyboard, mobile, stale fetch guard, and tests."
Decision table: state, ref, effect, or derived
Thing
Use
Why it matters
query, draft, selectedId
State
User/server can change it independently.
filteredItems, canSubmit, selectedItem
Derived render value
Duplicating it creates drift bugs.
DOM node, timer ID, latest request ID
Ref
Mutable value that should not cause a render.
Fetch, listener, subscription, focus, scroll
Effect
React is syncing with something outside render.
API result state
Status union
Make loading, success, empty, and error explicit.
The five patterns to type cold
Controlled input + derived filtering: no effect, no duplicated filtered state.