Skip to main content

Mid React Interview Questions

Curated Mid-level React interview questions for developers targeting mid positions. 30 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

30 questions
Q1:

How does React’s fiber architecture improve rendering compared to the old stack reconciler?

Mid

Answer

Fiber breaks rendering into small interruptible units, allowing React to pause, resume, or restart work. This prevents UI blocking, enables concurrency, and improves responsiveness during heavy updates.
Quick Summary: The old stack reconciler was recursive and synchronous — once started, it couldn't be interrupted. Fiber is a linked-list-based architecture where each unit of work (fiber) can be paused and resumed. This enables React to split rendering into small chunks, yield to the browser, and prioritize urgent updates (like user input) over non-urgent ones.
Q2:

What causes a component to re-render even if its props haven’t changed?

Mid

Answer

Re-renders occur when its parent re-renders, context value changes, state updates happen, dependencies trigger effects, or Strict Mode double-invokes renders in development.
Quick Summary: A component re-renders when: its own state changes, its parent re-renders (even without prop changes by default), or a Context it consumes changes. React.memo prevents re-renders from parent re-renders when props are shallowly equal. Many perceived "unnecessary" re-renders are actually from Context changes or parent cascades.
Q3:

Why should state updates depending on previous state use a callback function?

Mid

Answer

State updates are asynchronous and batched. Using callbacks like setCount(prev => prev + 1) ensures correct and predictable updates.
Quick Summary: setCount(count + 1) captures count from the closure — if multiple updates are batched, they all use the same stale count and only the last one "wins." setCount(prev => prev + 1) receives the latest state each time. Always use the callback form when the next state depends on the current state.
Q4:

How does React detect whether to reuse a DOM node or replace it?

Mid

Answer

React compares element type and key. If both match, React reuses the existing DOM node; otherwise, it unmounts and creates a new one.
Quick Summary: React checks the element type and key. Same type and key → React reuses the DOM node and updates changed props. Different type → React unmounts the old node and creates a new one from scratch. This is why conditionally rendering different component types at the same position loses state.
Q5:

Why do deeply nested state updates require immutable patterns?

Mid

Answer

React relies on reference comparison. Mutating nested objects keeps the same reference, preventing re-rendering. Spreads or immutable helpers create new references.
Quick Summary: Nested state (objects with nested objects) shares references. If you spread only the top level, nested objects still share the same reference — other parts of state pointing to them see the mutation. Deep cloning everything is expensive. Use Immer (or manual spreading at each level) to create truly independent state copies.
Q6:

What are layout shifts and how can React cause them?

Mid

Answer

Layout shifts occur when UI elements move unexpectedly. React may cause them due to conditional rendering inserts, async content changing structure, or Suspense fallbacks.
Quick Summary: Layout shifts happen when elements change position after initial paint — a classic CLS problem. React can cause this with useEffect that modifies layout (adding elements, changing sizes) after the browser painted. useLayoutEffect runs before paint, preventing visible layout jumps. Also, ensure images have explicit width/height attributes.
Q7:

Why can large Contexts cause performance issues?

Mid

Answer

When context value changes, all consuming components re-render, even those not using the changed fields, causing unnecessary work.
Quick Summary: Context triggers a re-render in every component that consumes it whenever the context value changes — even if the consumer only uses one field of the value. A large Context with many fields that update frequently causes widespread unnecessary re-renders. Split into smaller Contexts or use memoization strategies to limit propagation.
Q8:

How can unnecessary re-renders be prevented when using Context?

Mid

Answer

Split context into smaller contexts, memoize provider values, use useMemo/useCallback, or use selector-based libraries like Zustand or Jotai.
Quick Summary: Prevent Context re-renders by: splitting Context into smaller, focused Contexts (so components only subscribe to what they need), memoizing the Context value object with useMemo (so a new object isn't created every render unless values actually changed), or using Context.Provider around only the components that need it.
Q9:

What is lazy initialization of state in useState?

Mid

Answer

useState(() => ...) runs the initializer only once during the first render, avoiding repeated expensive computations on every render.
Quick Summary: useState(expensiveComputation()) runs expensiveComputation on every render — but only the first render uses the result. Lazy initialization passes a function: useState(() => expensiveComputation()) — React calls it only on the first render. Essential when initial state requires parsing large data or running heavy computation.
Q10:

When does React re-run useMemo?

Mid

Answer

useMemo runs again only when its dependencies change. If dependencies include unstable references, memoization becomes ineffective.
Quick Summary: useMemo re-runs when any value in its dependency array changes (using Object.is comparison). If dependencies are primitive values (strings, numbers), React can accurately detect changes. If dependencies are objects or arrays, a new reference always triggers re-computation — even if contents are the same. Stabilize object dependencies with their own useMemo.
Q11:

Why can memoized components still re-render?

Mid

Answer

Reasons include parent re-renders, non-stable prop references, context changes, or Strict Mode development re-renders.
Quick Summary: React.memo does a shallow comparison of props. If a parent passes a new object or function reference on each render (even with identical content), the memoized child still re-renders. Memoization only works when props are referentially stable — stabilize callbacks with useCallback and objects with useMemo.
Q12:

When should useCallback be preferred over inline functions?

Mid

Answer

Use useCallback when passing functions to memoized children or using them in dependency arrays. Otherwise, it adds unnecessary overhead.
Quick Summary: Use useCallback when a function is passed as a prop to a memoized child (React.memo) — without it, the child gets a new function reference each render and re-renders anyway. Don't use useCallback for every function — the memoization itself has a cost. Only apply it where referential stability actually prevents a re-render you care about.
Q13:

What is the purpose of startTransition?

Mid

Answer

startTransition marks updates as non-urgent so urgent updates like typing render first, keeping UI responsive under heavy workloads.
Quick Summary: startTransition marks state updates as non-urgent transitions. React immediately renders the current UI, then processes the transition update in the background — interruptible by higher-priority updates. If a more urgent update arrives (user typing), React pauses the transition, handles the urgent update, then resumes. Keeps the UI responsive during heavy renders.
Q14:

Why might React skip a render even after a state update?

Mid

Answer

React skips renders when the new state value is strictly equal to the previous state, avoiding unnecessary updates.
Quick Summary: React may bail out of a re-render if the state setter is called with the same value as the current state (Object.is comparison). React detects the value didn't change, skips rendering the component, and doesn't update children. This is automatic — you don't need to manually compare.
Q15:

How does React decide hydration priorities in React 18+?

Mid

Answer

React performs selective hydration, prioritizing interactive components, visible elements, and network-ready content.
Quick Summary: React 18 uses selective hydration: components inside Suspense boundaries are hydrated on demand. If the user interacts with a not-yet-hydrated component, React prioritizes hydrating that boundary immediately, even if other boundaries were registered first. User interaction signals high priority.
Q16:

What is the difference between Streaming SSR and Traditional SSR?

Mid

Answer

Traditional SSR sends HTML only after full rendering. Streaming SSR streams chunks as they are ready, improving first paint and user experience.
Quick Summary: Traditional SSR renders the full HTML on the server, sends it all, then hydrates the entire page before it's interactive. Streaming SSR (React 18) sends HTML chunks as they're ready, starting with the shell, then streaming in Suspense boundary content as their data resolves. Users see content faster and can interact sooner.
Q17:

How do server actions in React 19 differ from traditional API calls?

Mid

Answer

Server actions allow calling server-side functions directly from components without creating API endpoints, reducing boilerplate and enhancing security.
Quick Summary: Server actions in React 19 are async functions that run on the server, called directly from components — no need to create a separate API endpoint, set up fetch calls, or manage CSRF tokens. They're more ergonomic than REST calls and can directly mutate server data, then optionally revalidate the component tree.
Q18:

Why can server components not use browser APIs like window or document?

Mid

Answer

Server components run on the server where browser globals do not exist, making window or document unavailable.
Quick Summary: Server Components run in a Node.js environment on the server — they have no access to the browser's global APIs (window, document, navigator, localStorage). They execute before the page reaches the browser. Any browser-specific code must live in Client Components (marked with "use client") that run after hydration.
Q19:

What is the waterfall problem in SSR and how does Suspense fix it?

Mid

Answer

Waterfall fetching forces sequential loading. Suspense enables parallel data loading and rendering, eliminating delays.
Quick Summary: In traditional SSR, the server must fetch all data, render the full page, and send it — a sequential waterfall. Suspense lets the server stream the HTML shell immediately, then stream in each data-dependent section as its data resolves. Users see the page start loading instead of waiting for all data before seeing anything.
Q20:

Why must keys be stable and unique in React lists?

Mid

Answer

Keys help React correctly match elements during reconciliation. Unstable keys cause incorrect state retention or DOM mismatches.
Quick Summary: Stable keys let React match components between renders — the same key on the same item means React reuses its DOM node and state. Unstable keys (like Math.random()) cause React to destroy and recreate components every render — losing state and causing flicker. Keys must be unique among siblings, not globally.
Q21:

What is tearing in concurrent React?

Mid

Answer

Tearing occurs when parts of the UI read different versions of state during async rendering. React 18 fixes this using consistent rendering and batching.
Quick Summary: Tearing happens in concurrent rendering when React starts rendering with one version of external state, gets interrupted, and resumes with a different version — producing a UI where different parts show different states simultaneously. useSyncExternalStore prevents this by ensuring all reads in a render get the same snapshot of external state.
Q22:

Why do duplicated DOM nodes appear in lists during fast operations?

Mid

Answer

This happens due to unstable or incorrect keys, causing React to reuse DOM nodes incorrectly during rapid updates.
Quick Summary: This typically happens when keys aren't stable — React creates a new component instance instead of updating the existing one, momentarily showing both (old unmounting, new mounting). It can also happen with concurrent rendering where React renders multiple versions of the tree. Stable, unique keys are the primary fix.
Q23:

How does React ensure hooks run in the same order each render?

Mid

Answer

Hooks must be called at the top level and not inside conditions or loops, allowing React to map hook states consistently.
Quick Summary: React internally tracks hook calls by their order in the call stack — hook #1, hook #2, etc. This is why hooks can't be called conditionally or inside loops. If the order changes between renders, React reads the wrong hook's state. React enforces this with a linting rule (rules-of-hooks) and runtime checks in development.
Q24:

Why is returning different numbers of elements across renders problematic?

Mid

Answer

Inconsistent output causes layout shifts, hydration mismatches, and unstable UI trees during re-renders.
Quick Summary: React reconciles by comparing element type at each position. Returning 3 elements sometimes and 1 element other times means React sees different types at different positions and unmounts/remounts. This destroys component state unexpectedly. Use conditional rendering within a stable list structure to keep component identity consistent.
Q25:

What happens when two state updates occur in one event handler?

Mid

Answer

React batches them into a single render for better performance.
Quick Summary: React 18 batches all state updates in one event handler into a single re-render. So setName('Alice'); setAge(30); triggers one render, not two. React collects all updates from the handler, applies them all, then renders once with the final state. This is automatic in React 18 — no unstable_batchedUpdates needed.
Q26:

Why avoid defining styled-components or CSS-in-JS inside components?

Mid

Answer

It recreates component definitions on every render, hurting memoization and performance.
Quick Summary: Styled-components or CSS-in-JS objects defined inside a component are recreated on every render — generating new class names each time. This causes unnecessary style recalculations, can break animations, and adds garbage collection pressure. Define styles outside the component or with stable references at the module level.
Q27:

How does React maintain UI consistency during async rendering?

Mid

Answer

React builds a new UI tree in memory (double buffering) and commits only complete updates, ensuring consistent UI state.
Quick Summary: React uses the transition system and internal lanes to track which renders are in-progress. During concurrent rendering, React maintains consistency by committing all pending updates from one lane atomically before moving to the next. The commit phase is synchronous and non-interruptible — once React starts committing, it finishes.
Q28:

Why is reading DOM values inside render dangerous?

Mid

Answer

Render must stay pure. Reading DOM causes side effects and inconsistent behavior due to multiple render passes.
Quick Summary: Reading DOM properties (offsetHeight, getBoundingClientRect) during render causes layout thrashing — the browser must compute the layout immediately instead of batching it. In concurrent mode, React may run render multiple times, causing multiple forced layouts. Always read DOM in useLayoutEffect or event handlers, not in the render function.
Q29:

What is the purpose of useSyncExternalStore?

Mid

Answer

It ensures consistent subscriptions to external stores like Redux or Zustand during concurrent rendering, preventing tearing.
Quick Summary: useSyncExternalStore safely subscribes React components to external stores (Redux, Zustand, browser APIs). It provides a snapshot function (read current state) and a subscribe function (notify on change). React guarantees all reads use the same snapshot within a render — preventing the tearing problem in concurrent mode.
Q30:

Why memoize only expensive computations and not trivial ones?

Mid

Answer

Memoization has overhead. Overusing it slows performance, so only expensive repeated computations should be memoized.
Quick Summary: useMemo and useCallback have their own overhead: storing the cached value, tracking dependencies, comparing them each render. For trivial computations (adding two numbers, simple string operations), the memoization cost exceeds the computation cost. Only memoize when you've identified a real performance problem through profiling.

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