Skip to main content

Senior React Interview Questions

Curated Senior-level React interview questions for developers targeting senior positions. 40 questions available.

Last updated:

React Interview Questions & Answers

Skip to Questions

Welcome to our comprehensive collection of React interview questions and answers. This page contains expertly curated interview questions covering all aspects of React, from fundamental concepts to advanced topics. Whether you're preparing for an entry-level position or a senior role, you'll find questions tailored to your experience level.

Our React interview questions are designed to help you:

  • Understand core concepts and best practices in React
  • Prepare for technical interviews at all experience levels
  • Master both theoretical knowledge and practical application
  • Build confidence for your next React interview

Each question includes detailed answers and explanations to help you understand not just what the answer is, but why it's correct. We cover topics ranging from basic React concepts to advanced scenarios that you might encounter in senior-level interviews.

Use the filters below to find questions by difficulty level (Entry, Junior, Mid, Senior, Expert) or focus specifically on code challenges. Each question is carefully crafted to reflect real-world interview scenarios you'll encounter at top tech companies, startups, and MNCs.

Questions

40 questions
Q1:

How does React Fiber schedule work using lanes, and how does it prioritize rendering tasks?

Senior

Answer

React assigns each update to a lane based on priority. High-priority lanes like user input render first, while lower-priority lanes such as background transitions are deferred or interrupted, enabling concurrent rendering.
Quick Summary: React Fiber uses lanes to assign priority to work. Each fiber (unit of work) has a lane (urgent, transition, background). React's scheduler processes the highest-priority lane first. When a high-priority update arrives, React saves the current fiber's progress and starts the high-priority work. After, it resumes or restarts lower-priority work.
Q2:

What is the difference between Scheduler and Reconciler in React’s architecture?

Senior

Answer

Scheduler decides when work should run using priorities and interruption. Reconciler decides what to render by diffing and producing the fiber tree. Both combine to enable concurrency.
Quick Summary: The Scheduler determines when to run work — it uses the browser's message channel to schedule tasks between frames, preventing long tasks that block the main thread. The Reconciler determines what work to do — walking the fiber tree, comparing current and work-in-progress trees, and determining what DOM changes are needed.
Q3:

Why is React’s render phase interruptible but the commit phase is not?

Senior

Answer

Render is pure so it can be paused and resumed safely. Commit mutates the DOM and must run atomically to avoid inconsistent UI.
Quick Summary: The render phase is interruptible because it's pure computation — React calculates what changes are needed but makes no DOM mutations. Interrupting and restarting is safe. The commit phase makes real DOM mutations, fires refs, and runs effects — these are side effects that must complete atomically for the UI to be consistent.
Q4:

How do Suspense boundaries affect the rendering waterfall in SSR?

Senior

Answer

Suspense allows independently fetching components to resolve in parallel. SSR streams chunks as they are ready, removing sequential waterfalls.
Quick Summary: Suspense boundaries divide the SSR HTML into independently flushable sections. React can send the page shell, then stream each boundary as its data arrives — instead of waiting for all data. Each boundary resolves independently, eliminating the cascade where one slow query delays the entire page render.
Q5:

Why is mixing Server Components and Client Components beneficial in large apps?

Senior

Answer

Server Components reduce bundle size and handle data fetching. Client Components manage interactivity. The hybrid model improves performance and developer experience.
Quick Summary: Server Components handle data fetching and heavy rendering — no JS sent to the client for those parts. Client Components handle interactivity — only they download as JavaScript. This split minimizes JS bundle size while keeping data fetching efficient. Static parts render once on the server; interactive parts hydrate on the client.
Q6:

Why are Client Components not allowed to import Server Components?

Senior

Answer

Client bundles run in the browser; server-only code cannot be bundled and would cause unresolved modules and hydration failures.
Quick Summary: Client Components run in the browser — they're part of the JS bundle. Server Components run on the server and are never included in the client bundle. If a Client Component imported a Server Component, that Server Component code would need to be in the client bundle — leaking server-only code and defeating the purpose.
Q7:

What is the biggest challenge when migrating a large SPA to Server Components?

Senior

Answer

The hardest part is deciding boundary placement between server and client components without breaking interactivity, caching, or consistency.
Quick Summary: The biggest challenge is state management that assumed full client-side rendering — global stores, context that wraps everything, and effects for data fetching all need rethinking. Client/Server Component boundaries require careful design. Teams also struggle with understanding what code runs where and why some patterns break.
Q8:

How does React avoid tearing with external stores in concurrent mode?

Senior

Answer

React uses useSyncExternalStore to ensure all components read the same snapshot during render, preventing tearing during concurrent rendering.
Quick Summary: useSyncExternalStore ensures all reads of external state within a single render use the same snapshot. It works by re-rendering synchronously when the external store changes during a concurrent render — React detects the inconsistency and re-does the render synchronously to ensure a tear-free result.
Q9:

When is it better to replace Context with an event-driven state store?

Senior

Answer

Use Redux, Zustand, or Jotai when the app needs fine-grained re-render control, middleware, or cross-app communication. Context causes broad re-renders.
Quick Summary: Context is best for data that rarely changes (auth state, theme, locale). When many components need to react to frequent events (messages arriving, price changes), event-driven stores (Zustand, Redux, EventEmitter) are better — they let components subscribe to specific events and avoid re-rendering all Context consumers on every change.
Q10:

How do you prevent stale closures in hooks?

Senior

Answer

Use functional updates, stable references with useRef, and memoized callbacks via useCallback.
Quick Summary: Stale closures happen when a function captures a variable at one point in time and that variable later changes — but the function still uses the old value. Fix: include all used variables in useEffect dependencies (exhaustive-deps), use refs for values that should always be current without triggering re-renders, or use the functional update form of state setters.
Q11:

Why does React discourage heavy logic inside the render function?

Senior

Answer

Render may run multiple times. Heavy logic causes jank, blocks concurrency, and slows transitions.
Quick Summary: Heavy logic in render runs on every re-render — even if the data hasn't changed. Render should be a pure, cheap function that maps state to UI. Move heavy computations to useMemo (memoize results), useEffect (run after render), or outside the component entirely. Render is called frequently — keep it fast.
Q12:

How do you debug hydration mismatches in server-rendered apps?

Senior

Answer

Check non-deterministic code like random values, time-based content, missing useId, and async data mismatches.
Quick Summary: Hydration mismatches happen when server HTML doesn't match what the client renders. Debug by checking: Date.now() calls (different on server vs client), browser-only APIs accessed during render, non-deterministic data, or conditions based on environment. React 18 shows the specific mismatch in the console to pinpoint the diverging element.
Q13:

Why does React 19 introduce the use hook and how does it affect data fetching?

Senior

Answer

use unwraps promises directly inside components, eliminating useEffect + state boilerplate and improving SSR/RSC data workflows.
Quick Summary: The use hook in React 19 reads a Promise inline in the component body — React suspends if the Promise isn't resolved. Unlike useEffect, use works with Suspense automatically and can be called conditionally. It replaces the awkward useEffect + useState + loading state pattern for data fetching with a much cleaner syntax.
Q14:

When should you use startTransition vs useDeferredValue?

Senior

Answer

startTransition marks state updates as low-priority. useDeferredValue delays updating a derived value. Both prevent blocking urgent UI work.
Quick Summary: startTransition: use when triggering an expensive state update from an event — you control when the transition starts. useDeferredValue: use when receiving a value from outside (like a prop or external store) that you want to defer — you can't control when it updates, but you can delay the expensive re-render that responds to it.
Q15:

What is the purpose of the offscreen component in React?

Senior

Answer

Offscreen allows React to render components in the background and preserve state, enabling instant reveal without cost.
Quick Summary: The offscreen component (still experimental) lets React pre-render components in the background while they're hidden, then show them instantly without a re-render when they become visible. Useful for tab panels, accordions, or route transitions where you want instant reveal without rendering cost at reveal time.
Q16:

Why does React advise splitting components by responsibility rather than file size?

Senior

Answer

Too many responsibilities cause unpredictable re-renders, poor testability, and tight coupling. File size is not the real issue.
Quick Summary: Splitting by responsibility keeps components focused and small. A component doing many things re-renders when any of those things change — unnecessarily. Smaller, focused components only re-render when their specific concern changes. They're easier to memoize, test, and reuse. File size is a consequence of responsibility, not a reason to split.
Q17:

What is the risk of using derived state incorrectly?

Senior

Answer

Duplicated state leads to UI drift and inconsistency. Derived state should be computed on render, not stored.
Quick Summary: Derived state (state that mirrors props) gets out of sync when the parent prop updates but the local state doesn't reset. It creates two sources of truth. Instead, derive values directly from props using useMemo, or use a key prop to fully reset a component when its input changes. Minimize state — derive what you can.
Q18:

What causes React memory leaks in async operations?

Senior

Answer

Async tasks may update unmounted components. Use AbortController, cleanup functions, or transitions.
Quick Summary: Memory leaks happen when async operations (fetch, setInterval, subscriptions) complete after the component unmounts and try to update state — React warns about this. Fix: return a cleanup function from useEffect that cancels the operation (AbortController for fetch, clearInterval, unsubscribe). Always clean up async side effects.
Q19:

What is a hydration boundary and why is it important?

Senior

Answer

Hydration boundaries create isolated hydration zones, enabling selective hydration and reducing blocking time.
Quick Summary: A hydration boundary separates server-rendered content that can be progressively hydrated from the rest of the page. In React 18, Suspense boundaries serve as hydration boundaries — React can hydrate sections independently and prioritize based on user interaction. Without clear boundaries, the entire page must hydrate as one block.
Q20:

Why is tree-shaking more effective in Server Components?

Senior

Answer

RSC code never reaches the client; unused code stays on the server, making tree-shaking automatic.
Quick Summary: Server Components never ship to the client — their code, imports, and dependencies stay on the server. If a Server Component imports a 200KB library for data processing, that library doesn't affect the client bundle. Tree-shaking in bundlers eliminates unused imports; Server Components eliminate entire dependency branches from the client bundle.
Q21:

Why does React encourage referential stability for props to memoized children?

Senior

Answer

Unstable references break shallow comparison and force unnecessary re-renders. useCallback and useMemo ensure stability.
Quick Summary: React.memo does a shallow comparison of props. If a prop is a function or object, React compares references — not content. If the parent creates a new function or object each render, the reference changes, the memo comparison fails, and the child re-renders anyway. Stable references via useCallback and useMemo are what make memo effective.
Q22:

How does React determine when to flush pending updates?

Senior

Answer

React flushes updates based on event boundaries, transitions, microtasks, and scheduler priorities.
Quick Summary: React flushes pending updates when: an event handler completes (automatic batching), startTransition work completes, the Scheduler decides the task queue should flush, or you manually call flushSync. React 18 batches aggressively — it defers flushing to maximize batching, only flushing when it must produce visible output.
Q23:

Why do Suspense boundaries improve user-perceived performance even without SSR?

Senior

Answer

They show fallbacks early, preventing blank screens and minimizing layout shifts during async loading.
Quick Summary: Even without SSR, Suspense improves UX by showing a fallback immediately while a lazy-loaded component loads, preventing the blank flash users see otherwise. It declaratively handles loading states without useState loading flags. With startTransition, Suspense lets React show stale content while new content loads — no blank screens during navigation.
Q24:

What are islands of interactivity and how does React support them?

Senior

Answer

Islands hydrate only interactive parts of the page. React frameworks implement selective hydration for these components.
Quick Summary: Islands of interactivity mean only specific interactive parts of the page hydrate — the rest is static HTML. React supports this via Server Components (never hydrate), Suspense boundaries (hydrate independently), and React.lazy (load Client Components on demand). Frameworks like Astro implement full island architecture using React for the interactive islands.
Q25:

Why does local state for global concerns not scale?

Senior

Answer

Local state becomes duplicated and inconsistent across the app, requiring stores or context for synchronization.
Quick Summary: Local state for global concerns (user auth, theme, cart) lives in one component that must be a parent of all consumers — often creating deeply nested prop drilling or a mega Context. Multiple instances of the same state get out of sync. Global state (Context, Redux, Zustand) keeps one source of truth that all components read from.
Q26:

How does React prevent infinite loops when setters appear in useEffect deps?

Senior

Answer

State setters are stable references, so including them in dependency arrays does not trigger loops.
Quick Summary: React detects when a state setter is in the effect deps but the setter itself is stable (React guarantees setState functions are stable references). It won't re-run just because the setter is listed as a dep — the setter reference never changes. The loop only happens if the effect calls setter with a new value every time it runs.
Q27:

Why must reducers used with useReducer be pure?

Senior

Answer

Impure reducers cause unpredictable state and break React’s deterministic rendering model.
Quick Summary: Pure reducers make state transitions predictable and testable. React may call the reducer multiple times in Strict Mode or concurrent rendering to verify it's pure. An impure reducer (one with side effects or non-deterministic output) would produce different states on each call — causing unpredictable behavior.
Q28:

Why are mutations inside Context values harmful?

Senior

Answer

Mutating context values bypasses reference changes, causing silent inconsistencies and unnecessary re-renders.
Quick Summary: Mutations inside a Context value bypass React's change detection — the Context reference stays the same, so no consumer re-renders. Some see the new data, some see the old data, depending on when they last rendered. Always create new objects for Context values so React detects the change and triggers re-renders in all consumers.
Q29:

What is the biggest bottleneck for large grids or lists?

Senior

Answer

Re-rendering large DOM sections. Solution: virtualization such as react-window or react-virtual.
Quick Summary: Rendering thousands of rows in the DOM causes slow initial render, large memory usage, and slow layout calculations. The DOM simply wasn't designed for that many nodes. Virtualization (react-window, react-virtual) solves this by only rendering rows currently visible in the viewport — a constant number of DOM nodes regardless of list length.
Q30:

Why is hydration expensive for large SPAs?

Senior

Answer

Browser must attach listeners, validate markup, and resolve mismatches on the main thread.
Quick Summary: Hydration must attach event handlers to every interactive DOM node and run all components' initialization logic client-side — for a large SPA, this means executing a large JS bundle and running React reconciliation over the entire component tree. Progressive/selective hydration (React 18) helps by hydrating in priority order rather than all at once.
Q31:

How does React guarantee atomic state updates in concurrent mode?

Senior

Answer

React maintains current and work-in-progress trees. Commit replaces a whole subtree at once, ensuring atomicity.
Quick Summary: React uses lanes to batch related updates. When multiple state updates occur (within a transition, for example), React groups them into the same lane and commits them all together atomically. The commit phase is synchronous — no interruptions allowed. This guarantees users always see a consistent UI, never a half-applied state.
Q32:

Why can overusing React.memo degrade performance?

Senior

Answer

Memo adds comparison overhead. Often re-rendering is cheaper than shallow comparisons.
Quick Summary: React.memo adds comparison overhead on every parent render — it compares all props with Object.is. For cheap components that rarely have prop changes, this comparison costs more than just re-rendering. It also requires stabilizing all props with useCallback/useMemo, adding maintenance burden. Profile first; memoize only confirmed bottlenecks.
Q33:

How do you prevent race conditions in concurrent data fetching?

Senior

Answer

Use request versioning, AbortController, transitions, or server components to prevent out-of-order updates.
Quick Summary: Race conditions happen when two concurrent fetches (triggered by fast input changes) resolve in the wrong order. Fix with: AbortController to cancel the previous fetch when a new one starts, a cleanup flag in useEffect that discards results if the component re-rendered with new props, or a data-fetching library that handles this automatically.
Q34:

Why does React 19 recommend using actions instead of client API calls?

Senior

Answer

Actions execute server code directly, simplifying mutations, validation, error handling, and revalidation.
Quick Summary: React 19 Server Actions let you define server-side functions called directly from components. They handle optimistic updates, loading states, and error handling via useOptimistic and useFormStatus. The framework wraps network calls, serialization, and CSRF protection automatically — less boilerplate than manual fetch to a REST endpoint.
Q35:

How does React avoid inconsistent UI when multiple updates are queued?

Senior

Answer

React processes updates in an ordered queue using lane priorities to maintain determinism.
Quick Summary: React processes all queued updates in the same batch atomically before committing to the DOM. It uses the work-in-progress tree to compute the full result of all updates, then commits the final tree in one synchronous pass. Users never see intermediate states — the DOM goes from one consistent state to the next.
Q36:

Why are heavy animations better with requestAnimationFrame than React state?

Senior

Answer

React state does not synchronize with 60fps rendering. rAF ensures frame-perfect updates.
Quick Summary: React state updates are batched and processed asynchronously — not frame-synchronized. Animations updated via React state can miss frames and feel janky. requestAnimationFrame runs exactly once per frame, before the browser paints, synchronized with the display refresh rate. For smooth animations, update the DOM directly via requestAnimationFrame or use CSS transitions/animations.
Q37:

What causes zombie child components in concurrent React?

Senior

Answer

Outdated render passes create zombie children. React discards stale work to avoid committing them.
Quick Summary: Zombie children are child components that are still mounted after their parent's data no longer considers them valid — they have stale props and keep responding to state updates for data that may have been deleted. Happens when parent re-renders are delayed in concurrent mode. Proper key management and guarding against stale data in effects prevents this.
Q38:

When does React abort an entire render?

Senior

Answer

React aborts when higher-priority updates arrive or required data changes mid-render.
Quick Summary: React aborts an entire render when: an unhandled error is thrown during rendering (triggers the nearest error boundary), a higher-priority update preempts the current render (concurrent mode restarts with the new priority), or the component tree structure changes such that in-progress work is no longer valid.
Q39:

How does React enable background data loading without blocking UI?

Senior

Answer

Using Suspense, Streaming SSR, Offscreen rendering, and Server Components to load data in parallel.
Quick Summary: Suspense enables background data loading: React renders the current visible content while a data fetch runs in the background. When the data resolves, React seamlessly swaps in the new content without blocking the UI. With startTransition, the current UI stays visible and interactive during the background load — no loading spinner required.
Q40:

Why is Render-as-You-Fetch superior to Fetch-Then-Render?

Senior

Answer

It begins rendering while data loads, eliminating idle time and improving performance with Suspense and RSC.
Quick Summary: Fetch-Then-Render: load all data, then render — page is blank until data arrives. Render-as-You-Fetch: start rendering immediately and stream data in as it arrives using Suspense. Users see content progressively instead of a blank screen. Critical data unblocks the shell; secondary data streams in later. Net effect: faster perceived load time.

Curated Sets for React

No curated sets yet. Group questions into collections from the admin panel to feature them here.

Ready to level up? Start Practice