25 Senior React Interview Questions and Answers (2026)

Blog / 25 Senior React Interview Questions and Answers (2026)
React interview questions and answers

React is the most popular frontend developer framework and if you're a frontend or full stack developer it is a must know.

The median React engineer salary is $117,000 USD so if you're a senior engineer looking to ensure you land that next role these are the questions to study

Q1.
How does React handle events under the hood: where does it attach event listeners, and what changed about event delegation in React 17?

Senior

React doesn't attach a listener to every DOM node; it uses event delegation, attaching a few listeners at a root container and dispatching synthetic events to your handlers. In React 17 the delegation root moved from document to the root DOM container that you render into.

  • Synthetic events: Your onClick gets a SyntheticEvent, a cross-browser wrapper over the native event with a consistent API.

  • Delegation, not per-node listeners: React listens at one root and figures out which component handler to call as the event bubbles, which is more memory-efficient.

  • What changed in React 17:

    • Listeners now attach to the root container passed to ReactDOM.render instead of document.

    • This makes it safe to run multiple React versions or embed React in a larger app, since e.stopPropagation() in one tree won't leak across trees.

  • Practical note: Event pooling (reusing the synthetic event object) was removed in React 17, so you no longer need e.persist().

Q2.
Why can't hooks be called inside loops or conditions, and how does React track hook state internally using an array/linked list?

Senior

React doesn't identify hooks by name; it relies on them being called in the same order on every render, storing each hook's state in a list indexed by call order. Loops, conditions, or early returns can change that order, so React would read the wrong slot, which is why the Rules of Hooks forbid them.

  • How React tracks hooks:

    • Each component's fiber holds an ordered list (a linked list of hook objects); a cursor advances by one each time a hook is called.

    • On the first render it builds the list; on re-renders it walks the same list in the same order to retrieve each hook's saved state.

  • Why order must be stable: Position, not name, maps a call to its state. If a conditional skips a hook, every later hook shifts by one slot and reads another hook's value.

  • The rule:

    • Call hooks only at the top level of a component or custom hook, never inside loops, conditions, or nested functions.

    • To do something conditionally, put the condition inside the hook (e.g. inside useEffect) rather than around the hook call.

  • Enforcement: The eslint-plugin-react-hooks lint rule catches violations at dev time.

javascript
// Wrong: order changes when `id` is falsy if (id) { const [data] = useState(null); // hook count varies between renders } // Right: hook always runs; condition lives inside const [data, setData] = useState(null); useEffect(() => { if (id) fetchData(id); }, [id]);

Q3.
Explain the stale closure problem in useEffect. How does the dependency array solve this, and what happens if you omit a dependency?

Senior

A stale closure happens when an effect (or callback) captures variables from the render in which it was created, so it keeps reading their old values instead of the latest ones. The dependency array fixes this by telling React to re-run the effect (and re-capture fresh values) whenever those dependencies change.

  • Why it happens:

    • Every render creates new functions that close over that render's props and state. An effect runs against the snapshot it was defined in.

    • If the effect never re-runs, it permanently sees the values from the first render.

  • How the dependency array solves it:

    • React compares each dependency (via Object.is) to the previous render; if any changed, it re-runs the effect with the new closure.

    • So the listed values are always current inside the effect.

  • If you omit a dependency:

    • The effect won't re-run when that value changes, so it operates on stale data (bugs like a counter stuck at its initial value).

    • The eslint-plugin-react-hooks exhaustive-deps rule flags missing dependencies.

  • Escape hatch: if you need the latest value without re-running, use a ref or the updater form setCount(c => c + 1).

javascript
// Stale: closes over count from first render only useEffect(() => { const id = setInterval(() => setCount(count + 1), 1000); return () => clearInterval(id); }, []); // count missing -> always 0 + 1 // Fixed: updater avoids depending on count useEffect(() => { const id = setInterval(() => setCount(c => c + 1), 1000); return () => clearInterval(id); }, []);

Q4.
How does the new React Compiler (React Forget) change how we think about performance optimization, and does it make useMemo and useCallback obsolete?

Senior

The React Compiler (formerly React Forget) automatically memoizes components and values at build time, so you rarely write manual memoization. It largely makes useMemo and useCallback unnecessary for performance, but it doesn't make them obsolete in every case.

  • What it does:

    • Analyzes your components and inserts fine-grained memoization automatically, caching values and re-rendering only what truly changed.

    • Relies on the Rules of React (purity, no mutation) to safely reason about dependencies.

  • Shift in mindset: You write idiomatic, readable code and let the compiler optimize, instead of manually tuning re-renders.

  • Does it kill useMemo/useCallback?:

    • For performance memoization: largely yes, the compiler covers it.

    • But useMemo still matters for semantic stability (e.g. a stable reference passed to a non-React API or used as an effect dependency).

    • Code that breaks the Rules of React won't be optimized, so manual hooks remain a fallback.

Q5.
What are the performance tradeoffs of using the Context API for frequently changing values?

Senior

Context triggers a re-render in every consumer whenever its value changes, regardless of whether a given consumer uses the part that changed. For frequently changing values this can cause widespread, expensive re-renders.

  • The core problem:

    • All components reading the context re-render on any value change; Context has no built-in selector to subscribe to a slice.

    • A new object/array passed as value each render forces consumers to re-render even if data is identical.

  • Mitigations:

    • Split contexts: separate stable data from fast-changing data so updates are scoped.

    • Memoize the value with useMemo to avoid identity churn.

    • Wrap consumers in React.memo where helpful.

  • When it's the wrong tool: For high-frequency updates (cursor position, form keystrokes shared widely), prefer a state library with selector-based subscriptions (Zustand, Redux, Jotai).

Q6.
How do you identify and fix "wasted" or unnecessary re-renders in a large application?

Senior

Find wasted renders by measuring with the React DevTools Profiler, then fix by stabilizing props, memoizing components, and narrowing where state lives so updates don't cascade.

  • Identify:

    • Use the Profiler's "Why did this render?" and highlight-updates feature to see which components re-render and why.

    • Look for components re-rendering when their visible output didn't change.

  • Common causes:

    • New object/array/function literals created in render and passed as props.

    • State or context too high in the tree, re-rendering large subtrees.

  • Fixes:

    • Wrap pure children in React.memo; stabilize props with useMemo/useCallback.

    • Lift state down (colocate) or split context so updates are scoped.

    • Use the children prop pattern so a changing parent doesn't force static children to re-render.

  • Measure first: don't add memoization blindly, it has its own overhead.

Q7.
What are the costs of over-using React.memo, and why shouldn't we wrap every single component in it?

Senior

`React.memo` isn't free: it adds a memoization layer with a props comparison on every render plus extra memory, so wrapping everything can cost more than it saves.

  • Comparison overhead: Every render runs a shallow props compare; for components that almost always re-render anyway, you pay the comparison and still re-render.

  • Memory cost: React retains the previous props and rendered output to compare against, which adds up across many components.

  • It's defeated by unstable props: New object/array/function literals or inline children break the shallow check, so `memo` does nothing unless you also stabilize props with `useMemo`/`useCallback`.

  • Hidden complexity: You drag in `useCallback`/`useMemo` everywhere to make it work, adding noise and more dependency arrays to maintain.

  • When it actually helps: Components that are expensive to render, re-render often, and receive stable props: profile first, memoize the real hotspots.

Q8.
What is the difference between useTransition and useDeferredValue, and when would you use one over the other to improve perceived performance?

Senior

Both keep the UI responsive during expensive updates by marking work as low-priority, but `useTransition` wraps the state update that triggers the work, while `useDeferredValue` wraps a value you receive and lets a lagging copy of it drive the expensive render.

  • `useTransition`:

    • Gives you `startTransition` to mark a state update as non-urgent, plus an `isPending` flag you can show during it.

    • Use when you control the update that schedules the heavy work (e.g. tab switches, filtering on a controlled input you own).

  • `useDeferredValue`:

    • Takes a value and returns a deferred version that can lag behind during heavy renders; no `isPending`, but you can compare deferred vs current to show staleness.

    • Use when you only have the value, not the setter (e.g. a prop from a parent, or a third-party-driven value).

  • Choosing: Own the state update, reach for `useTransition`; only have a downstream value to defer, reach for `useDeferredValue`.

Q9.
Explain Suspense for data fetching: how does it change the way we handle loading states compared to the traditional loading-spinner pattern?

Senior

Suspense lets a component "suspend" while its data is still loading, and React shows the nearest `<Suspense>` fallback declaratively instead of you wiring up manual `isLoading` flags in each component.

  • Traditional pattern: Each component tracks `isLoading`/`error`/`data` and conditionally renders a spinner: state and loading logic are scattered and easy to get inconsistent.

  • Suspense pattern:

    • The data source signals "not ready" by throwing a promise; React pauses that subtree and renders the `fallback` of the closest `<Suspense>` boundary.

    • Loading UI moves up to a declarative boundary, so you place fallbacks where they make UX sense and coordinate multiple loads with one spinner.

  • Benefits: Separates loading UI from fetching logic, avoids waterfalls when combined with concurrent features, and pairs with error boundaries for failures.

  • Caveat: You need a Suspense-enabled data layer (frameworks like Next.js, React Query, Relay, or `use()` with a promise): you can't just await in a component.

Q10.
What is 'Concurrent Rendering', and how does it allow React to remain responsive during heavy UI updates?

Senior

Concurrent rendering is React's ability to prepare ('render') UI updates in the background and interrupt, pause, or abandon that work, so high-priority updates like typing stay responsive while heavy renders proceed without blocking the main thread for long.

  • Interruptible rendering: Unlike the old synchronous render that ran to completion and blocked, React can pause a low-priority render to handle urgent input, then resume.

  • Priority-based scheduling: Urgent updates (clicks, keystrokes) jump ahead of non-urgent ones marked via transitions.

  • Opt-in via features, not automatic: You enable concurrency through APIs like `startTransition`, `useDeferredValue`, and `Suspense`; the root must be created with `createRoot`.

  • Why it stays responsive: Work is split and yielded back to the browser, so a heavy re-render doesn't freeze input handling or animations.

Q11.
Explain the concept of Transitions in React 18 and how useTransition helps maintain UI responsiveness during heavy re-renders.

Senior

A transition marks a state update as non-urgent: React 18 renders it in the background at low priority while urgent updates (typing, clicks) interrupt and go first. `useTransition` wraps such an update so a slow re-render never blocks immediate feedback.

  • Urgent vs transition updates: Urgent updates reflect direct interaction and must feel instant; transition updates (filtering a large list, switching views) can lag slightly without hurting UX.

  • What `useTransition` returns: `[isPending, startTransition]`: wrap the heavy state update in `startTransition`, and use `isPending` to show a subtle loading indicator.

  • How it keeps the UI responsive: The transition render is interruptible, so a new keystroke aborts the in-progress heavy render and processes the urgent update first.

  • Common use case: Keep an input snappy (urgent) while an expensive filtered list updates (transition).

javascript
const [isPending, startTransition] = useTransition(); function handleChange(e) { setQuery(e.target.value); // urgent: input stays responsive startTransition(() => { setResults(filterBigList(e.target.value)); // non-urgent, interruptible }); } return <>{isPending && <Spinner />}<List items={results} /></>;

Q12.
Compare the HOC pattern with the Render Props pattern, and why have both largely been replaced by Hooks?

Senior

HOCs and Render Props both solve the same problem (sharing reusable, stateful logic across components) by wrapping or injecting, just with different mechanics. Hooks largely replaced both because they share logic without adding wrapper components or nesting.

  • HOC pattern:

    • A function that takes a component and returns an enhanced one: withData(Component).

    • Logic is shared via injected props; composes statically.

  • Render Props pattern:

    • A component takes a function as a prop (or child) and calls it with the shared state: <Data>{value => ...}</Data>.

    • Logic is shared dynamically at render time.

  • Shared downsides:

    • "Wrapper hell": deeply nested trees that are hard to read and debug.

    • HOCs cause prop name collisions and obscure where props come from; render props create awkward nesting pyramids.

  • Why Hooks won:

    • A custom Hook (useData()) shares stateful logic with no extra component layers.

    • Logic is reused as plain function calls, easier to compose and test.

Q13.
What is the purpose of useImperativeHandle when used with forwardRef?

Senior

useImperativeHandle lets a child component customize the value (the imperative API) exposed to a parent through a ref, instead of exposing the raw DOM node. It's used with forwardRef so the parent can call specific methods you choose to expose.

  • Customizes the ref handle: Rather than giving the parent the underlying element, you expose a controlled object like { focus, scrollToTop }.

  • Requires forwardRef: The ref is passed in as the second argument; useImperativeHandle(ref, () => ({...}), deps) defines what that ref points to.

  • Use it sparingly: Imperative APIs go against React's declarative flow; prefer props/state. Good fits are things like focusing an input, triggering an animation, or exposing scroll controls.

javascript
const Input = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus(), })); return <input ref={inputRef} />; }); // Parent: inputRef.current.focus()

Q14.
What is React Fiber, and how does it differ from the old stack reconciler in terms of how it handles updates?

Senior

React Fiber is the reconciliation engine introduced in React 16: a complete rewrite that represents work as a linked list of "fiber" nodes so rendering can be split into units, paused, prioritized, and resumed. The old stack reconciler processed the whole tree recursively and synchronously, which it couldn't interrupt.

  • Old stack reconciler: Used the call stack to recurse through the tree in one uninterruptible pass, so a large update could block the main thread and cause jank.

  • Fiber is incremental: Work is broken into small units React can pause, yield to the browser, and resume, keeping the UI responsive.

  • Supports prioritization: High-priority updates (user input) can interrupt lower-priority ones, enabling concurrent features like useTransition.

  • Two phases: An interruptible render/reconcile phase that builds the work-in-progress tree, then a synchronous commit phase that applies changes to the DOM.

Q15.
What is automatic batching in React 18, and how does it change the way state updates are processed compared to older versions?

Senior

Automatic batching in React 18 means multiple state updates are grouped into a single re-render, even when they happen inside promises, timeouts, or native event handlers. Before 18, batching only applied inside React event handlers, so updates elsewhere triggered a separate render each.

  • Batching = one render for many updates: Calling several setters together produces a single re-render instead of one per setter, improving performance.

  • What changed in 18:

    • Pre-18: batching only inside React-managed event handlers.

    • React 18: batching also applies in setTimeout, promises, async callbacks, and native events.

  • Enabled by createRoot: You get automatic batching when you use the new root API; the legacy ReactDOM.render keeps old behavior.

  • Opt out when needed: Wrap a setter in flushSync to force an immediate, synchronous render between updates.

Q16.
Explain the difference between the Render phase and the Commit phase in the Fiber architecture, and which one is interruptible.

Senior

Fiber splits a render into two phases: the render phase computes what changed (interruptible), and the commit phase applies those changes to the DOM (synchronous and uninterruptible).

  • Render phase (reconciliation):

    • React builds a work-in-progress Fiber tree, calls components, and diffs to figure out the effects needed.

    • It is interruptible: React can pause, abort, or restart this work, which is what enables concurrent features like time-slicing and priority-based scheduling.

    • Because it can be replayed, this phase must be side-effect free (this is why render must be pure).

  • Commit phase:

    • React applies the computed mutations to the real DOM in one synchronous, uninterruptible pass so the UI is never shown half-updated.

    • Runs lifecycle/effects: componentDidMount/componentDidUpdate, refs, and useLayoutEffect synchronously; useEffect is flushed shortly after.

  • Key takeaway: only the render phase is interruptible, which is why side effects belong in the commit phase, not in render.

Q17.
Explain the difference between the Virtual DOM and the Shadow DOM. Why does React use one but not necessarily the other?

Senior

They are unrelated concepts that share a name. The Virtual DOM is React's in-memory diffing abstraction for efficient updates; the Shadow DOM is a native browser feature for encapsulating a component's DOM and styles. React uses the Virtual DOM by design but does not require the Shadow DOM.

  • Virtual DOM:

    • A JavaScript representation of the UI used to diff and batch updates to the real DOM.

    • Purely a React/library construct, not a browser standard.

  • Shadow DOM:

    • A native Web Components standard that attaches an isolated subtree to an element, scoping its markup and CSS so styles don't leak in or out.

    • About encapsulation, not diffing or performance.

  • Why React uses one but not the other:

    • React's core value is the declarative update model, which the Virtual DOM enables.

    • React handles style/component isolation at the JS level (component scope, CSS-in-JS, CSS Modules), so it doesn't need the Shadow DOM, though it can render into one if desired.

Q18.
Why does React need to traverse the entire UI tree during a re-render, and what are the performance implications of this?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q19.
What is 'Hydration' in React, and why do 'Hydration Mismatch' errors occur? How does React 18/19's selective hydration improve this?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q20.
What is the fundamental difference between a Server Component and a Client Component, and why can't you use useState in a Server Component?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q21.
Explain the difference between React Server Components and traditional Server-Side Rendering (SSR). Do they solve the same problem?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q22.
What is "Streaming SSR" and how does it improve Time to First Byte (TTFB)?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q23.
How does useSyncExternalStore help in keeping React state in sync with external data sources?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q24.
What problem does the use hook solve, and how does it differ from standard hooks regarding where it can be called?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Q25.
Explain how the useOptimistic hook works. Why is it better than manually managing 'loading' and 'success' states for UI responsiveness?

Senior
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.