DEV Community

Cover image for 25 React Interview Questions 2026 (With Answers) — Hooks, React 19, Concurrent Mode
Aindrila Bhattacharjee
Aindrila Bhattacharjee

Posted on • Originally published at mockexperts.com

25 React Interview Questions 2026 (With Answers) — Hooks, React 19, Concurrent Mode

React powers the frontends of Facebook, Instagram, Netflix, Airbnb, and thousands of top tech startups. With over 40% market share among frontend frameworks, React remains the most sought-after skill for frontend developers in 2026.

Whether you're preparing for a startup loop or a FAANG panel, this comprehensive list of 25 React interview questions—updated with React 19 paradigms and modern performance patterns—will ensure you stand out.


🚀 Fundamentals (Questions 1-8)

1. What is the Virtual DOM and how does React use it?

The Virtual DOM (VDOM) is an in-memory, lightweight representation of the real browser DOM.

When a component's state or props change, React updates the Virtual DOM first. The process follows three main steps:

  1. Render: React generates a new Virtual DOM tree representing the updated UI.
  2. Reconciliation (Diffing): React compares the new Virtual DOM tree with the previous one using a highly optimized O(n) diffing algorithm.
  3. Commit: React computes the minimal set of changes needed and applies them to the real DOM (via batching), minimizing expensive layout recalculations in the browser.

2. What is JSX and why does React use it?

JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like structures directly inside your JavaScript code.

JSX is not valid browser JavaScript; it must be compiled (typically using Babel or SWC) before execution. Under the hood, JSX compiles to standard function calls. In modern React (17+), JSX compiles using the automatic JSX transform:

// What you write:
const element = <h1 className="title">Hello World</h1>;

// What it compiles to:
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('h1', { className: 'title', children: 'Hello World' });
Enter fullscreen mode Exit fullscreen mode

React uses JSX because it combines UI markup and rendering logic in one place, enhancing readability, developer experience (DX), and enabling compile-time syntax checks.


3. Explain the difference between controlled and uncontrolled components.

  • Controlled Components: The form input data is handled by React state. React serves as the "single source of truth" for the input values, updating the state on every keystroke.
  • Uncontrolled Components: The form data is handled directly by the browser DOM. React accesses the DOM node when needed (usually on submission) using useRef.

Controlled Component Example:

import { useState } from 'react';

function ControlledInput() {
  const [value, setValue] = useState('');

  return (
    <input 
      type="text" 
      value={value} 
      onChange={(e) => setValue(e.target.value)} 
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Uncontrolled Component Example:

import { useRef } from 'react';

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`Input Value: ${inputRef.current.value}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" ref={inputRef} />
      <button type="submit">Submit</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Verdict: Controlled components are preferred for most real-world scenarios because they enable instant field validation, dynamic disabling of submit buttons, and enforce predictable state.


4. What are React keys and why are they important?

Keys are unique, stable strings assigned to elements in a list. They serve as an identity card for React's reconciliation engine.

<ul>
  {items.map((item) => (
    <li key={item.id}>{item.name}</li>
  ))}
</ul>
Enter fullscreen mode Exit fullscreen mode

Why they are critical:

  • Reconciliation Guide: When list items change, are reordered, or deleted, keys tell React which specific element in the real DOM should be updated, kept, or destroyed.
  • Indices as Keys Warning: Using the array index (key={index}) as a key is an anti-pattern for dynamic lists. If list items are reordered, filtered, or inserted at the top, React will mismatch the DOM state with the component instances, causing visual glitches and rendering bugs.

5. What is prop drilling and how do you avoid it?

Prop drilling occurs when data is passed from a parent component down through multiple layers of nested child components that do not actually need the data, simply to deliver it to a deeply nested target component.

How to avoid it:

  1. Component Composition: Pass components or children down directly, keeping the state logic in the parent:

    <ParentLayout>
      <DeepChild user={user} />
    </ParentLayout>
    
  2. React Context API: Ideal for global, low-frequency state updates like themes, user authentication, or language settings.

  3. State Management Libraries: Using lightweight global stores like Zustand, Redux Toolkit, or Jotai.


6. Explain the React component lifecycle in functional components.

In modern functional React, lifecycle methods are replaced by Hooks (primarily useEffect):

  • Mounting: The component is rendered for the first time.

    useEffect(() => {
      console.log('Component mounted');
      // API calls, event listeners setup
    }, []); // Empty dependency array
    
  • Updating: The state or props of the component change, triggering a re-render.

    useEffect(() => {
      console.log('Dependency changed, component updated');
    }, [dependency]); // Runs on mount and whenever the dependency changes
    
  • Unmounting: The component is removed from the DOM.

    useEffect(() => {
      return () => {
        console.log('Component will unmount');
        // Clean up subscriptions, intervals, event listeners
      };
    }, []);
    

7. What is the difference between state and props?

Feature State Props
Definition Component's local, internal memory Data passed down from a parent component
Mutability Mutable (via updater function like setState) Immutable (read-only within the receiver)
Scope Local to the component that defined it Dynamic; controlled by the parent component
Re-render Triggers component and children to re-render Triggers component and children to re-render

8. How does React handle events differently from vanilla JavaScript?

React abstracts standard DOM events through SyntheticEvents:

  1. Cross-Browser Compatibility: React wraps native browser events in a SyntheticEvent instance, ensuring that event properties behave identically across Chrome, Safari, Firefox, and Edge.
  2. CamelCase Naming: Event handlers are named using camelCase (e.g., onClick, onSubmit) rather than lowercase (e.g., onclick, onsubmit).
  3. Event Delegation: Instead of attaching event handlers to individual DOM nodes, React attaches a single listener to the root container of your application (#root) and bubbles events up for maximum memory efficiency.

⚓ Hooks Deep Dive (Questions 9-15)

9. Explain useState vs useReducer. When would you use each?

  • useState is designed for managing simple, independent state variables (numbers, strings, booleans, or shallow objects).
  • useReducer is suited for complex state trees, multi-step forms, or state where the next state depends heavily on the previous state.

useReducer Example:

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT': return { count: state.count + 1 };
    case 'DECREMENT': return { count: state.count - 1 };
    default: return state;
  }
};

const [state, dispatch] = useReducer(reducer, { count: 0 });
Enter fullscreen mode Exit fullscreen mode

Rule of Thumb: Use useReducer when you have 3+ interrelated state variables or when your state-updating logic starts leaking out of your component and needs to be isolated for clean testing.


10. What are the rules of hooks?

React enforces two strict rules for all Hooks:

  1. Only call Hooks at the top level: Do not call Hooks inside loops, conditional statements, or nested functions. This ensures React maps state variables in the exact same call sequence on every re-render.
  2. Only call Hooks from React functions: Call them from React functional components or custom hooks. Do not call Hooks from regular JavaScript utility functions.

Tip: Enable eslint-plugin-react-hooks in your project to automatically catch rule violations at compile time.


11. How does useEffect cleanup work?

The cleanup function is the function returned from inside your useEffect callback. It executes:

  1. Immediately before the effect re-runs (to clean up residual side effects from the previous render).
  2. When the component unmounts from the DOM.
useEffect(() => {
  const handleResize = () => console.log(window.innerWidth);
  window.addEventListener('resize', handleResize);

  // Cleanup Function:
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

Failing to clean up event listeners, WebSocket channels, or polling intervals results in memory leaks that degrade web application performance.


12. What is useMemo and when should you use it?

useMemo caches (memoizes) the computed result of an expensive calculation, recalculating it only when its specified dependency values change.

const heavyCalculation = useMemo(() => {
  return performComplexSort(dataSet);
}, [dataSet]); // Recalculates only when dataSet changes
Enter fullscreen mode Exit fullscreen mode

When to use it:

  • Heavy Computations: CPU-intensive tasks like sorting large arrays, parsing complex JSON structures, or rendering complex mathematical graphs.
  • Referential Equality: Caching object references passed down as props to children to prevent child re-renders.
  • Warning: Do not wrap every variable in useMemo. Overusing it adds garbage collection and dependency check overhead, which can actually slow down simple rendering.

13. Explain useCallback and its relationship with React.memo.

  • useCallback memoizes the function reference itself, preventing it from being re-created on every single render.
  • React.memo is a higher-order component that prevents a child component from re-rendering unless its props change.
// Parent Component
const handleButtonClick = useCallback(() => {
  console.log("Clicked!");
}, []); // Reference remains identical across renders

return <MemoizedButton onClick={handleButtonClick} />;
Enter fullscreen mode Exit fullscreen mode

Without useCallback, a standard child component wrapped in React.memo will still re-render on every parent change because inline functions are re-created with new memory references on every tick, failing shallow prop equality checks.


14. What is useRef and how does it differ from useState?

  • useRef returns a mutable object whose .current property persists throughout the lifecycle of the component. Crucially, updating .current does not trigger a component re-render.
  • useState preserves state values across renders and explicitly triggers a re-render whenever the state updater function is called.
// useRef: Perfect for capturing DOM references directly
const inputElement = useRef(null);
const focusInput = () => inputElement.current.focus();
Enter fullscreen mode Exit fullscreen mode

Use useRef when storing values that don't affect the visual output of the UI (e.g., keeping track of timer intervals, click counts, or previous state references).


15. How do you create a custom hook? Give an example.

A custom hook is a standard JavaScript function whose name starts with "use" and which calls other React hooks internally. It allows you to extract complex stateful logic out of your UI components.

Reusable Custom Fetch Hook Example:

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let active = true;
    setLoading(true);

    fetch(url)
      .then((res) => res.json())
      .then((data) => {
        if (active) {
          setData(data);
          setLoading(false);
        }
      });

    return () => {
      active = false; // Aborts stale responses
    };
  }, [url]);

  return { data, loading };
}
Enter fullscreen mode Exit fullscreen mode

⚡ Performance & Advanced (Questions 16-21)

16. How do you optimize React app performance?

When discussing optimizations, focus on these five core pillars:

  1. Code Splitting: Dynamic importing of routing pages using React.lazy() and <Suspense />.
  2. Memoization: Strategic application of React.memo(), useCallback(), and useMemo() to halt unnecessary renders.
  3. List Virtualization: Utilizing libraries like react-window or react-virtualized to only render the visible DOM nodes of a 10,000+ item list.
  4. Debouncing / Throttling: Delaying heavy search API calls or scroll listeners.
  5. State Colocation: Moving local states as close as possible to the components that actually use them to avoid sweeping parent-child re-render chains.

17. What is code splitting and how does React implement it?

Code splitting is the practice of breaking down a large, single production JavaScript bundle into multiple smaller chunks that are loaded dynamically (on-demand) as the user navigates your application.

import { lazy, Suspense } from 'react';

// Heavy component loaded lazily:
const DashboardChart = lazy(() => import('./DashboardChart'));

function App() {
  return (
    <Suspense fallback={<div>Loading Dashboard Chart...</div>}>
      <DashboardChart />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

This reduces the initial page load time, maximizing core web vitals and overall page speeds.


18. Explain React's reconciliation algorithm.

The reconciliation algorithm determines how React updates the real DOM. Instead of comparing whole trees with O(n³) complexity, React uses heuristic rules to run at O(n) speeds:

  1. Different Element Types: If two elements have different types (e.g., swapping a <div> for a <section>), React tears down the entire subtree and mounts a completely new one from scratch.
  2. Same Element Types: If two elements have the same type, React compares the attributes/props, updates only the changed attributes, and recursively moves down to diff the children.
  3. Keys Strategy: When diffing children, React relies on keys to quickly match items in the old list with items in the new list, minimizing DOM rearrangements.

19. What are React Server Components (RSC)?

React Server Components (RSC) are a new architectural paradigm where components can render entirely on the server.

  • Server Components (Default): Render on the server, have zero impact on the client-side bundle size, can directly fetch database query results or secure API keys, and cannot use client hooks like useState or useEffect.
  • Client Components (Opt-in via "use client"): Hydrated on the client-side, fully interactive, and are free to use hooks, event listeners, and standard browser APIs.

RSCs allow you to build complex web apps with minimal client JavaScript, faster page load speeds, and improved SEO.


20. What is Suspense and how is it used in React 19?

Suspense is a built-in React component that lets you declaratively handle loading states across your application. It acts as an orchestrator, catching promises thrown by children fetching data or code-splitting.

In React 19, Suspense is deeply integrated with the framework:

  • It supports streaming server-side rendering (SSR), sending static sections to the browser immediately and hydration-streaming dynamic sections as data fetches resolve.
  • It handles resource loading (stylesheets, scripts, fonts) seamlessly, ensuring components render only after critical dependencies are ready in the browser.

21. Explain React's error boundaries.

An Error Boundary is a React component that catches JavaScript errors anywhere in its child component tree, logs the crash diagnostics, and displays a backup fallback UI instead of letting the entire web page crash blank.

Note: Error boundaries must currently be written as class components, as functional hooks do not support componentDidCatch or getDerivedStateFromError yet.

import React from 'react';

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true }; // Updates state to render fallback UI
  }

  componentDidCatch(error, errorInfo) {
    console.error("ErrorBoundary caught an error:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h2>Oops! Something went wrong.</h2>;
    }
    return this.props.children;
  }
}
Enter fullscreen mode Exit fullscreen mode

🗃️ State Management & Patterns (Questions 22-25)

22. Compare Context API, Redux, and Zustand.

  • Context API: Built directly into React. Excellent for static or low-frequency updates (e.g., dark mode, user session). However, it is not optimized for rapid updates, as any value change forces all consumer components downstream to re-render.
  • Redux Toolkit: The classic, battle-tested standard. Strong ecosystem, middleware integration, and top-tier debugging (DevTools). However, it can be boilerplate-heavy for simple states.
  • Zustand: A modern, ultra-lightweight state manager. Based on a publish-subscribe model, it has zero boilerplate, does not wrap your app in providers, and allows hooks-based selective consumption, preventing unwanted renders.

23. What is the React Compiler (React Forget)?

The React Compiler is a new build-time tool introduced in React 19.

Traditionally, developers had to manually optimize components using useMemo, useCallback, and React.memo to avoid wasteful re-renders. The React Compiler automatically compiles your component tree at build time, injecting auto-memoization logic where it identifies that outputs won't change.

This brings the performance of hand-optimized code to standard React code without manual hook pollution.


24. Explain the render props pattern vs custom hooks.

  • Render Props Pattern: Sharing code by passing a function as a prop, letting the receiving component decide how to render details:

    <MouseTracker render={(mouse) => (
      <h1>Mouse position is: {mouse.x}, {mouse.y}</h1>
    )} />
    
  • Custom Hooks: Extracts stateful logic directly into clean functions, avoiding component nesting ("wrapper hell"):

    const { x, y } = useMousePosition();
    return <h1>Mouse position is: {x}, {y}</h1>;
    

Verdict: While render props are still useful for layout components, custom hooks are the standard choice for sharing stateful business logic.


25. How do you handle authentication in a React app?

Authentication typically follows this robust pattern:

  1. State Storage: Store the user session/JWT token in a secure global state (like a Context Provider or Zustand store).
  2. API Requests: Attach the JWT token inside HTTP headers (using an Axios Request Interceptor) for all outgoing requests.
  3. Route Protection: Wrap sensitive dashboard routes inside a ProtectedRoute component:

    function ProtectedRoute({ children }) {
      const { isAuthenticated } = useAuth();
      return isAuthenticated ? children : <Navigate to="/login" />;
    }
    
  4. Token Refreshing: Set up interceptors to automatically listen for 401 Unauthorized statuses and refresh expired tokens securely in the background.


🎯 Elevate Your Frontend Prep with MockExperts

Reading the answers is just the first step. To pass high-stakes frontend interview loops, you need to communicate these concepts clearly under pressure.

Try MockExperts' proctored AI Interview Simulator to practice live coding, dynamic React 19 system design, and behavioural rounds with instant, actionable feedback.

👉 Practice a Free Mock Interview now: MockExperts.com

Top comments (0)