Interview bookGroundworkInterview book
10 / 27 written chapters

R4·FE · Part 2 · The technical rounds

The frontend round that is not about React

If the title on the job description is Frontend Engineer rather than Full Stack, the loop adds a round that React cannot answer. It is CSS you have to reason about rather than recall, the rendering pipeline underneath the framework, accessibility as a requirement rather than a nice-to-have, and how you fetch and cache data. Candidates who are strong in R4 and lose the offer usually lose it here.

6 questions19 follow-ups4 traps9 min read
Length
30–45 min, often folded into R4
Who
Frontend lead
Decides
Whether "frontend" means React or means the browser
Fail mode
Framework fluency, no platform knowledge
Asked at
  • product
  • saas
  • agency
  • service
Practise this round as a mock →

Everything open, top to bottom.

07On this page
  1. Why is this element not where I put it? Walk me through stacking contexts.
  2. Centre a box. Then: flexbox or grid, and why?
  3. What actually happens between a style change and a pixel on screen?
  4. Make this accessible. What do you actually check?
  5. How do you fetch and cache data on the client?
  6. How do you test a frontend?
  7. The rest of the frontend round
R4·FE.1

Why is this element not where I put it? Walk me through stacking contexts.

What they are really testingWhether CSS is something you reason about or something you try until it works.

The answer they want is that z-index is not global. An element only competes with siblings inside its own stacking context, and a parent creates one the moment it gets position with a z-index, or transform, opacity below 1, filter, will-change, or isolation: isolate.

Which is why a modal with z-index: 9999 still renders behind a header: the modal sits inside a parent that has a transform on it, and the whole context is below the header\'s. No z-index inside that context can escape it.

  • The fix is to move the element out of the context — a portal to body — or to remove whatever created the context.
  • The tell of experience is saying transform creates one. Almost everybody knows about position; the animation-created context is the one that actually costs people an afternoon.
The answer that loses the room

"I would just use z-index: 99999." It is the answer that says you have never had to find out why the last one did not work.

The same answer at three levels
2–3 years

The bar: know that z-index needs positioning, and that it orders siblings rather than the whole page.

“z-index only applies to positioned elements — anything other than position: static — and it orders an element against its siblings. So if I set a z-index and nothing moves, the first thing I check is whether the element is positioned at all. If that is not it, I would go up the tree in devtools, because I have seen a parent affect this and I would want to find which one.”

Why it passes here: Correct, and honest about the edge of what you know. If you have not met stacking contexts yet, saying you would inspect the parents is a much better answer than inventing a mechanism — the interviewer will usually just tell you, and you have shown a debugging instinct instead of a gap.

5–7 years

The bar: name the stacking context, list what creates one, and fix it structurally rather than numerically.

“z-index is not global — it is scoped to a stacking context. A parent creates one when it has a position with a z-index, but also with transform, opacity below 1, filter, will-change, or isolation: isolate. Once a parent has one, nothing inside can escape it: the whole subtree is ordered as a single unit against the parent’s siblings. Which is why a modal with z-index: 9999 still renders behind the header — its parent has a transform on it from an animation, and that entire context sits below the header’s. The fix is not a bigger number, it is a portal that renders the modal on body, outside that context.”

Why it lands: transform creating a context is the tell. Almost everyone knows about position; the animation-created one is the one that actually costs somebody an afternoon, so naming it says you lost that afternoon.

10+ years

The bar: treat it as a component-boundary problem the system should prevent, not a bug to fix again.

“Once you have debugged that twice you stop fixing it per instance. Overlays — modals, tooltips, dropdowns, toasts — belong in one layer owned by the design system: a single portal mount point and a small named scale for the layers, so no feature team is writing raw z-index numbers. The failure is not z-index, it is that every team is allowed to invent one. The deliberate version of the same mechanism is worth knowing too — isolation: isolate on a component root creates a context on purpose, so a child can never leak out of it into someone else’s stacking order.”

Why this is the level above: Identical knowledge, aimed at prevention instead of at the bug on screen. That reframe — from “how do I fix this” to “how does the system stop this happening” — is precisely the thing a frontend lead is trying to detect, because it is what they will be hiring you to do.

They will push with
  1. What else creates a stacking context?
  2. Where would you render a tooltip?
  3. How do you debug this in devtools?
How did you do?
R4·FE.2

Centre a box. Then: flexbox or grid, and why?

What they are really testingWhether you have a model of layout or a set of remembered incantations.

The centring is a warm-up — display: grid; place-items: center, or flex with both axes. What they are actually asking is the second half.

  • Flexbox is one dimension. Content decides the sizes; you are distributing space along a row or a column. Reach for it for a toolbar, a list of chips, anything where the number of items varies and they should flow.
  • Grid is two dimensions. The container decides the shape; children go into it. Reach for it for page layout, a card grid, or anything where things must line up across both rows and columns.

The sentence that lands: flex sizes from the content out, grid sizes from the container in. Then say where you have used each.

2026 note

Worth having ready: gap works in flexbox too, which surprises people who learned it before 2021 and are still using margins; and minmax() with auto-fit is the one-line responsive card grid that removes most breakpoints.

They will push with
  1. How would you make that responsive without media queries?
  2. What is the difference between auto-fit and auto-fill?
  3. Where does subgrid help?
How did you do?
R4·FE.3

What actually happens between a style change and a pixel on screen?

What they are really testingWhether you know the pipeline your framework sits on. This is the frontend equivalent of the event loop question.

Style → layout → paint → composite. Which stage your change triggers is what decides whether an animation is smooth:

  • Layout (also called reflow) — anything geometric: width, height, top, font-size, adding a node. The most expensive, because it can invalidate the whole subtree.
  • Paint — color, background, box-shadow, border-radius. Skips layout, still costs.
  • Composite only — transform and opacity. Handled on the compositor, off the main thread. This is why every smooth animation on the web is written with those two and not with left and top.

The other half is layout thrashing: reading a geometric property (offsetHeight, getBoundingClientRect) after a write forces the browser to flush layout synchronously. Do it in a loop and you have turned a list render into a freeze. Batch the reads, then the writes.

The answer that loses the room

Animating left/top and blaming React for the jank. The framework never touched it; the property choice did.

They will push with
  1. Why is transform cheaper?
  2. What does will-change actually do, and why not put it everywhere?
  3. How would you find a layout thrash in devtools?
How did you do?
R4·FE.4

Make this accessible. What do you actually check?

What they are really testingWhether accessibility is something you do or something you have heard of. At a product company with real users this is a scored requirement, not a bonus.

Four things, in the order they matter, and each one is checkable in a minute:

  • Semantics first. A <button> is focusable, activates on space and enter, and announces itself. A <div onClick> does none of that, and no amount of ARIA fully fixes it. The best ARIA is the ARIA you did not need.
  • Keyboard. Tab through the whole flow with the mouse untouched. Every interactive thing reachable, focus visible, order matching the visual order, and no trap — except in a modal, where a trap is exactly what you want, plus Escape to close and focus returning to the trigger.
  • Names and state. Every control has an accessible name (a label, or aria-label when there is no visible text), and anything that toggles says so: aria-expanded, aria-pressed, aria-current.
  • Contrast and motion. 4.5:1 for body text, 3:1 for large text and UI boundaries. Respect prefers-reduced-motion.

Then say how you check: axe or Lighthouse in CI catches the mechanical half, and the other half only shows up when you tab through it yourself.

Say it like this

The first thing I do is delete ARIA. Most of what people add is fixing a div that should have been a button — so semantics first, then tab through the flow with the mouse away, then run axe in CI so the mechanical failures stop coming back. On the last project that meant landmarks and heading order, focus return on modal close, and a contrast pass that changed two tokens.

The answer that loses the room

Reciting "I use semantic HTML and ARIA labels" with no example. Interviewers ask this precisely because it is the easiest thing to claim and the easiest to check — one follow-up about focus management separates the two groups.

They will push with
  1. How do you handle focus when a modal closes?
  2. What does aria-live do, and when have you needed it?
  3. How do you test this in CI?
How did you do?
R4·FE.5

How do you fetch and cache data on the client?

What they are really testingWhether you have outgrown useEffect-and-fetch. This is the most common gap in an otherwise strong React candidate.

The answer starts by naming what raw useEffect fetching does not give you, because that list is the reason libraries exist: no caching, no deduplication when three components ask for the same thing, no revalidation, no request cancellation when the inputs change, and race conditions when a slow early response lands after a fast late one.

  • TanStack Query or SWR for server state. Key, fetcher, stale time — and the mental split that matters: server state is cached remote data with an owner elsewhere; client state is what the user is doing right now. Most state-management arguments are people using one tool for both.
  • Stale-while-revalidate — serve the cached value instantly, refetch in the background, replace if it changed. Instant screens without lying for long.
  • Optimistic updates — apply the change locally, keep the previous value, roll back on failure. Say the rollback part; that is what separates having read about it from having shipped it.
  • Invalidation — the hard half. Which keys does this mutation invalidate? Getting that wrong is why a list does not refresh after a create.

And in the App Router the question shifts: fetching moves to the server component and the client library handles what stays interactive. Know which half you are talking about — R4 covers the server side.

The answer that loses the room

"I use useEffect with fetch and a loading state." True for a two-year candidate; at five or more it says you have not run into the race, which means you have not shipped anything with a search box in it.

They will push with
  1. How do you cancel an in-flight request?
  2. What is your stale time and why?
  3. How do you handle pagination and infinite scroll?
  4. Where does this go in the App Router?
How did you do?
R4·FE.6

How do you test a frontend?

What they are really testingWhether your tests would survive a refactor.

The line to hold is test behaviour, not implementation. A test that asserts on internal state or a component\'s props breaks on every refactor and protects nothing.

  • React Testing Library for components, queried the way a user finds things — role and accessible name first, then text, then data-testid as an admission of defeat. Queries by role also mean a component with no accessible name fails the test, which quietly enforces the previous question.
  • MSW to intercept requests at the network layer rather than mocking the fetch call. The component stays untouched, and the same handlers serve the tests and local development.
  • Playwright for the few flows that must never break — sign in, checkout, the thing that takes the money. Not for coverage.
  • What not to write: snapshot tests of whole trees, which are updated without being read; and unit tests of components that only assert they rendered.
2026 note

If they ask about coverage numbers, say what the number is for rather than quoting one. Ninety percent on a codebase where all the tests assert on mocks is worth less than forty percent that covers the money path.

They will push with
  1. How do you test a hook?
  2. How do you handle a component that fetches?
  3. What is flaky in your suite and why?
How did you do?
R4·FE.7

The rest of the frontend round

What they are really testingBreadth. One clean sentence each.

  • How do you keep the bundle small? Route-level code splitting first, then measure before you optimise — a bundle analyser, and the one dependency that is a third of the bundle.
  • Images. Correct format and size, explicit dimensions so they do not shift layout, lazy below the fold, eager and preloaded for the LCP element.
  • What causes a bad CLS? Images without dimensions, fonts swapping, and content injected above what is already on screen.
  • Forms. Validate on blur and on submit, not on every keystroke; show the error next to the field; keep the submit button enabled and explain the failure rather than disabling it silently.
  • XSS on the frontend. Never build HTML from user input; if you must render HTML, sanitise it. dangerouslySetInnerHTML is named that way deliberately. A CSP is the seatbelt, not the fix.
  • Browser support. Know what the product actually supports before you answer — the honest answer is "whatever our analytics say", not a list of browsers.

Finished this round?

0 of 27 rounds read in this book