Skip to main content

Frontend engineer role-specific interview questions

30 questions on role-specific for frontend engineer candidates. Expect prompts such as “Why does React's diffing algorithm need the key prop?” and “Debugging a stale closure bug inside useEffect”, each with a worked answer outline and the follow-ups interviewers push on.

Questions

Showing 1 to 25 of 30 role-specific questions (page 1 of 2).

As asked

Explain why React's list diffing algorithm is O(n) rather than the theoretical O(n^3) tree diff, and what role the key prop plays in that. What actually goes wrong at runtime when you use an array index as a key for a reorderable list?

Sample answer outline

Covers the two heuristics React uses: same type at same position stays, and keys short-circuit cross-position matching. Explains that index-as-key causes wrong component state to be kept alive (e.g., controlled input values, animation state) when items are inserted or reordered because React maps the same key to a different data item.

Expect these follow-ups

  • When is it actually safe to use an array index as a key?
  • How does key affect component lifecycle, specifically when does a component unmount vs. receive new props?
reactdiffingkeysperformancelists

As asked

You have a useEffect that sets up a WebSocket listener. Inside the handler, you read a piece of state that always shows its initial value even after the state has changed. Walk me through why this happens and at least two different ways to fix it.

Sample answer outline

Explains that the closure captures the value of state at the time the effect ran, and because the dependency array was empty the effect never re-ran. Fixes include adding state to the dependency array (and re-subscribing), using a ref to hold the latest value, or using the functional updater form of setState.

Expect these follow-ups

  • What is the downside of putting the subscription in the dependency array if setup is expensive?
  • How would you write a custom useEvent hook that always sees the latest state without triggering re-subscription?
reacthooksclosuresuseEffectdebugging

As asked

A page has an LCP of 4.2 seconds. Walk me through the systematic process you would use to identify the bottleneck, assuming you have access to Chrome DevTools, WebPageTest, and the CrUX report. What are the most common LCP culprits in a React app served from a CDN?

Sample answer outline

Should identify the four LCP sub-parts from the Performance panel: TTFB, resource load delay, resource load duration, and element render delay. Common React culprits include render-blocking JS bundles delaying FCP, hero images not preloaded, images not sized correctly (causing layout to wait), and SSR hydration blocking paint.

Expect these follow-ups

  • What is fetchpriority=high on an image tag and when would you use it over a link rel=preload?
  • How does a large JavaScript bundle contribute to poor LCP even when the LCP element is an image?
performancelcpcore-web-vitalsreactcdn

As asked

You have a context that holds both a user object and a theme string. Any component consuming the context re-renders when either value changes. A form deep in the tree subscribes only to theme but re-renders on every user update. What is happening and what are two different architectural fixes?

Sample answer outline

Context compares value by reference; if the provider re-renders with a new object literal the reference changes even if the data is the same. Fixes: split into separate UserContext and ThemeContext so consumers only subscribe to what they need, or use useMemo on the context value to stabilize the reference. Should also mention React.memo on consumers and the use-context-selector pattern.

Expect these follow-ups

  • How does the use-context-selector library work under the hood to enable fine-grained context subscriptions?
  • When would you reach for Zustand or Jotai instead of Context and what is the key difference?
reactcontextperformancestate-managementre-renders

As asked

You are building a custom dropdown component because the native select element cannot be styled to match the design. Walk me through which ARIA attributes you would add, why you would add them, and what keyboard interaction pattern is required to make it accessible.

Sample answer outline

Trigger button gets aria-haspopup=listbox and aria-expanded. The listbox container gets role=listbox. Each option gets role=option with aria-selected. Focus management: Arrow keys move focus through options, Enter selects, Escape closes and returns focus to trigger. Should mention the ARIA Authoring Practices Guide combobox pattern versus listbox pattern.

Expect these follow-ups

  • What does aria-live do and when would you use aria-atomic versus aria-relevant?
  • How would you test this component with a screen reader in CI without a real browser?
accessibilityariakeyboard-navigationhtmlcomponents

As asked

Give me a concrete rule of thumb for choosing between CSS Grid and Flexbox, then describe a layout where using Flexbox would be wrong and Grid is the right tool, and another where the opposite is true.

Sample answer outline

Rule of thumb: Flexbox is one-dimensional (a row or a column), Grid is two-dimensional. Grid is right for a page-level layout with named areas or a card grid that needs both row and column alignment. Flexbox is right for a navigation bar where items should stretch to fill available space along one axis and wrap naturally.

Expect these follow-ups

  • What is subgrid and what problem does it solve that nested grids cannot?
  • How does the fr unit in Grid differ from flex-grow in Flexbox?
cssgridflexboxlayout

As asked

Explain the difference between useMemo and useCallback, when each is actually worth using, and describe a situation where adding useMemo makes performance worse rather than better.

Sample answer outline

useMemo memoizes a computed value; useCallback memoizes a function reference. useCallback is mainly useful when passing stable function references to memoized children (wrapped in React.memo) to prevent re-renders. useMemo is useful for expensive computations. Using them everywhere adds overhead: every render must compare deps array and store the memoized value. Should profile before adding memoization.

Expect these follow-ups

  • What does React.memo do and how does it interact with useCallback?
  • When would you use the React Compiler instead of manual memoization?
reacthooksperformancememoization

As asked

You have designed a complex loading-to-success animation for a fintech product. What does a good engineering handoff include?

Sample answer outline

A good handoff includes the states, triggers, timing, easing curves, delays, interruption rules, and reduced-motion variant. It should specify which elements are layout-affecting and which should animate on transform or opacity for performance. Provide a prototype or exported values, but do not assume a video is enough for implementation. Include acceptance criteria so QA can tell whether the motion is correct. Candidates who understand handoff speak in implementation-ready constraints while still protecting the intention of the motion.

Expect these follow-ups

  • How do you handle an engineer saying the animation is too expensive?
  • What should happen if the success response arrives before the loading animation finishes?
  • How would you QA this across devices?
handoffperformanceqa

As asked

Walk me through what happens inside React's Fiber reconciler when setState is called on a deeply nested component. How does it decide which parts of the tree to re-render, and how does it differ from the old stack reconciler?

Sample answer outline

Strong answers cover the two-phase commit model (render/reconcile phase vs. commit phase), how Fiber represents each component as a linked list of work units, time-slicing via the scheduler, and how priority lanes determine which updates are processed first. Should contrast with the old synchronous stack reconciler that could not be interrupted.

Expect these follow-ups

  • How do concurrent features like useTransition interact with the Fiber priority system?
  • What does React actually put in the alternate Fiber tree and why?
reactfiberreconciliationrenderinginternals

As asked

You need to write a utility type UnwrapPromise<T> that extracts the resolved value type of a Promise. Walk me through how you would use a conditional type with the infer keyword to implement this, and what happens when T is a nested Promise like Promise<Promise<string>>.

Sample answer outline

Strong answer writes T extends Promise<infer U> ? U : T and notes this only unwraps one level. Extends to recursive conditional type for deep unwrapping. Should also mention how distributive conditional types work when T is a union.

Expect these follow-ups

  • How would you make the unwrapping work recursively without hitting TypeScript's recursive type limit?
  • What is the difference between a distributive and a non-distributive conditional type?
typescriptconditional-typesgenericstype-inference

As asked

Explain what a block formatting context is and how creating one on a container causes it to stretch around floated children instead of collapsing. What are three different CSS properties that trigger a new BFC, and what side effects do they have?

Sample answer outline

Covers that a BFC is a self-contained layout region where floats and clear interact only within it. Triggering properties include overflow: hidden (clips content), display: flow-root (no side effects, purpose-built), and contain: layout. Should note that float: left/right and position: absolute/fixed also trigger BFC.

Expect these follow-ups

  • Why did developers use overflow: hidden as a clearfix hack for years rather than display: flow-root?
  • How does the stacking context relate to the formatting context, and what creates a new stacking context?
cssbfclayoutfloatsrendering

As asked

Describe three concrete situations in a typical React page that each produce CLS, and for each one explain the correct fix. What is the layout shift score formula in terms of impact fraction and distance fraction?

Sample answer outline

Common causes: images and iframes without explicit width/height or aspect-ratio causing reflows when they load; dynamic content (ads, banners, toast notifications) inserted above existing content; web fonts causing FOUT that changes text metrics. Score = impact fraction times distance fraction summed across animation frames.

Expect these follow-ups

  • How does the CSS aspect-ratio property help prevent layout shift for images loaded lazily?
  • What is font-display: optional and how does it trade FOUT for guaranteed zero CLS?
performanceclscore-web-vitalscsslayout

As asked

Your app has an INP of 380ms measured in CrUX. Walk me through how you would use the PerformanceObserver API and Long Animation Frame API to identify which interactions are slow, and then explain the main categories of fix.

Sample answer outline

Should explain that INP measures the worst interaction latency (input delay plus processing time plus presentation delay). Tooling: PerformanceObserver with 'event' entry type, Long Animation Frames (LoAF) in Chrome 123+. Fixes fall into: reducing input delay (avoid long tasks before event fires), reducing processing time (break up synchronous work, defer non-visual updates), and reducing presentation delay (avoid forced style recalcs after state updates).

Expect these follow-ups

  • What is the difference between INP and FID, and why did Google replace FID with INP?
  • How does React's concurrent mode help with INP and what are its limits?
performanceinpcore-web-vitalsjavascriptprofiling

As asked

Explain what happens at the webpack level when you use dynamic import() in a React app with React.lazy. How does webpack decide chunk boundaries, what goes into the async chunk versus the main bundle, and what network request does the browser make at runtime?

Sample answer outline

Covers that webpack statically analyzes import() calls to produce async chunks. The main bundle gets a small chunk loader stub; when the lazy component is needed the browser fetches the chunk file by its content-hashed name. Shared dependencies between chunks go into a shared chunk via SplitChunksPlugin. Should mention that React.lazy wraps this in a Suspense boundary that renders the fallback while the chunk loads.

Expect these follow-ups

  • What is prefetching versus preloading a chunk and how do you configure webpack to emit the correct link hints?
  • How does React.lazy behave differently with SSR and what do you need to do in Next.js to make it work server-side?
webpackbundlingcode-splittingreactperformance

As asked

Starting from the moment the browser receives the first bytes of an HTML document, walk me through every major step in the rendering pipeline up to pixels on screen. Where in this pipeline does JavaScript execution fit, and what does render-blocking actually mean?

Sample answer outline

Covers: bytes to characters to tokens to DOM nodes (HTML parser), parallel CSS parsing building CSSOM, render tree = DOM + CSSOM, layout (geometry calculations), paint (draw calls), compositing (GPU layers). Parser-inserted scripts block HTML parsing because they may call legacy DOM mutation APIs synchronously. CSS blocks rendering because render tree requires CSSOM. Async and defer scripts do not block parsing.

Expect these follow-ups

  • What is speculative or preload scanning and why did browsers add it?
  • When does the browser promote an element to its own compositor layer and why is that not always good?
browserrenderingcssperformancehtml

As asked

You are starting a mid-sized React app with complex async flows and developer teams who need clear data mutation trails. Walk me through the tradeoffs between Zustand and Redux Toolkit that would inform your choice, including devtools, middleware, and team ergonomics.

Sample answer outline

RTK gives a structured pattern (slices, thunks, RTK Query), strong devtools with time-travel, and an opinionated approach that scales with large teams. Zustand is minimal boilerplate, easy to colocate with components, great for smaller or mid-size apps, but provides weaker conventions so large teams can diverge. Should cover RTK Query vs. React Query for server state.

Expect these follow-ups

  • How would you handle optimistic updates in RTK Query versus React Query?
  • What is the distinction between client state and server state and why does it matter for library choice?
state-managementzustandreduxreactarchitecture

As asked

Your API returns a response that is either a success with a data payload or an error with a code and message. How would you model this with a TypeScript discriminated union, and how does TypeScript's type narrowing work inside if statements that check the discriminant?

Sample answer outline

Define two interfaces sharing a common literal-typed field like status: 'success' or status: 'error'. In a conditional that checks result.status === 'success', TypeScript narrows to the success branch with data available. Should mention exhaustive checking with a never type in a switch default branch.

Expect these follow-ups

  • What is the never type and how do you use it to guarantee exhaustive handling of a union?
  • How would you handle a third possible state like loading or pending in the same union?
typescriptdiscriminated-unionstype-narrowinggenerics

As asked

Explain what React Server Components are, how they differ from traditional SSR, what they cannot do that Client Components can, and what concrete benefit they provide over fetching data in a useEffect.

Sample answer outline

RSCs run only on the server and never ship their own JavaScript to the client, reducing bundle size. They can directly access databases and secrets. They cannot use browser APIs, event handlers, or stateful hooks. Versus useEffect data fetching: no waterfall between render and fetch, no loading spinner for the initial shell, and the serialized React tree is streamed incrementally.

Expect these follow-ups

  • What is the RSC payload format and how does the client reconcile it with existing client tree?
  • Why can a Server Component render a Client Component but a Client Component cannot render a Server Component?
reactserver-componentsssrnext-jsperformance

As asked

Walk me through how Next.js 13+ App Router uses React Suspense to stream HTML to the browser. What HTTP mechanism is used, what renders first, and how does hydration work for content that arrives later in the stream?

Sample answer outline

Next.js uses HTTP chunked transfer encoding. The shell (layout, static content, Suspense fallbacks) is sent immediately. When an async segment resolves, Next.js sends a script tag that inlines the RSC payload and JavaScript that swaps the fallback for the real content. Hydration of each chunk happens incrementally as chunks arrive.

Expect these follow-ups

  • What is the loading.tsx file in the App Router and how does it interact with Suspense boundaries?
  • What happens to streaming when you deploy to a platform that does not support chunked responses?
next-jsstreamingssrsuspensereact

As asked

A designer gives you a blue button with white text. What WCAG 2.1 success criteria apply to the text contrast and to the button focus indicator? How do you compute the contrast ratio and what are the thresholds for AA versus AAA?

Sample answer outline

Text contrast is SC 1.4.3 (AA 4.5:1 for normal text, 3:1 for large text) and 1.4.6 (AAA 7:1). Focus indicator is SC 2.4.11 in WCAG 2.2 requiring minimum area and contrast. Contrast ratio formula: (L1 + 0.05) / (L2 + 0.05) where L1 and L2 are relative luminance values computed from sRGB.

Expect these follow-ups

  • What does SC 1.4.11 Non-text Contrast require for the button border or background in its default state?
  • How would you automate contrast checking in a Storybook or Playwright test pipeline?
accessibilitywcagcolor-contrastdesigntesting

As asked

Why is a scroll event listener a poor choice for implementing image lazy loading, and how does IntersectionObserver solve the problem? What browser-native alternative exists today and when would you still write a custom observer?

Sample answer outline

Scroll listeners fire on the main thread on every scroll event, causing jank because they block rendering. IntersectionObserver is asynchronous: the browser tracks intersection state in the compositor and delivers batched callbacks to the main thread only when visibility thresholds are crossed, so the cost is paid far less frequently than on every scroll tick. Native loading=lazy on img and iframe is now supported widely and handles the common case. Custom observers are needed for more complex triggers like a percentage of element visible, or for non-image elements like analytics exposure tracking.

Expect these follow-ups

  • What are the rootMargin and threshold options on IntersectionObserver and how would you use them for a pre-load-before-visible effect?
  • How does native loading=lazy interact with fetch priority for above-the-fold images?
browser-apisperformancelazy-loadingintersection-observerimages

As asked

Explain the philosophical difference between React Testing Library and Enzyme. What does test behavior, not implementation mean in practice, and give me an example of a test that Enzyme would pass but that hides a real user-facing bug.

Sample answer outline

Enzyme gives direct access to component internals (instance, state, shallow rendering of child components). This lets tests pass after refactoring that breaks the real UI. RTL queries mirror how a user perceives the DOM (by label, role, text), so tests only pass if the rendered output is correct from the user's perspective. Example: Enzyme shallow render passes even if a child component that renders the actual button is broken.

Expect these follow-ups

  • What is the difference between getBy, queryBy, and findBy queries in Testing Library?
  • How do you test a component that makes a fetch call, and what is the recommended way to mock fetch in RTL?
testingreact-testing-libraryreactcomponent-testing

As asked

Explain what tree shaking is, how bundlers detect dead code exports in ES modules, and why the same technique does not work reliably with CommonJS require() modules.

Sample answer outline

Tree shaking relies on static import/export syntax so bundlers can build a module dependency graph at compile time and remove exports that are never imported. CommonJS require() is dynamic (can be conditional, computed) so the bundler cannot statically determine which exports are used. Should mention that side-effect annotations in package.json sideEffects: false allow more aggressive tree shaking.

Expect these follow-ups

  • What is the sideEffects field in package.json and how would you configure it for a component library?
  • How does Rollup's approach to tree shaking differ from webpack's?
bundlingtree-shakingwebpackes-modulesperformance

As asked

Your Next.js app logs a hydration mismatch warning in the console. Walk me through what hydration mismatch means, the most common causes in a server-rendered React app, and how you would systematically find and fix the offending component.

Sample answer outline

Hydration mismatch means the HTML rendered on the server differs from what React expects to render on the client during the reconciliation pass. Common causes: Date.now() or Math.random() called during render, conditional rendering based on window/localStorage that only exists client-side, browser extensions injecting DOM nodes. Fix: use useEffect for client-only state, useId for stable IDs, suppressHydrationWarning as a last resort.

Expect these follow-ups

  • What does React's new hydration error overlay in development mode show you that the old warning did not?
  • How does the suppressHydrationWarning prop work and when is it appropriate to use it?
reactssrhydrationnext-jsdebugging

As asked

Explain the difference between a task queue and the microtask queue in the browser event loop. Where do Promises, setTimeout, requestAnimationFrame, and queueMicrotask fit? What renders between a task and the next microtask?

Sample answer outline

Each event loop iteration processes one task, then drains the entire microtask queue before moving on. Promise .then callbacks go into the microtask queue. setTimeout creates a task. requestAnimationFrame callbacks run before the render step which happens after microtasks and before the next task. Nothing renders between a promise chain; the browser only gets a chance to render between tasks.

Expect these follow-ups

  • What is a long task in web performance terms and how does it relate to INP?
  • How does scheduler.yield() help break up long synchronous React work?
browserevent-loopjavascriptperformancemicrotasks