Hiprup
·11 min read

40 React Interview Questions Every Developer Should Know (2026)

The React interview questions hiring managers actually ask — hooks, rendering, state, performance, and the rules of hooks — each with a short, practical answer.

React continues to dominate frontend hiring, and the interview questions have matured to match. This guide covers the 40 questions you'll encounter most — from fundamentals through hooks, rendering internals, and performance optimization — with concise answers you can actually talk through in an interview.


React fundamentals

1. What is React and what problem does it solve?

React is a JavaScript library for building component-based user interfaces. It solves the problem of keeping the UI in sync with changing application state by introducing a declarative model: you describe what the UI should look like for any given state, and React figures out how to efficiently update the DOM to match.

2. What is the virtual DOM and why does it matter?

The virtual DOM is a lightweight JavaScript representation of the real DOM tree. When state changes, React renders a new virtual DOM tree, diffs it against the previous one (reconciliation), and applies only the minimal set of changes to the real DOM. This avoids expensive full repaints and keeps UIs fast.

3. What is JSX?

JSX is a syntax extension that looks like HTML inside JavaScript. Babel (or the Next.js compiler) transforms it to React.createElement calls. JSX is not required but is universally used because it makes component trees readable. Key difference from HTML: classclassName, forhtmlFor, and expressions go in {}.

4. What is a React component?

A component is a reusable, self-contained piece of UI. Modern React uses function components (plain JavaScript functions that return JSX) — class components still exist but are rarely written in new code. Components accept props and maintain optional local state.

5. What is the difference between props and state?

props are read-only values passed from a parent component — a component cannot change its own props. state is local, mutable data owned by a component; updating it via the setter triggers a re-render. The golden rule: data flows down through props, events flow up through callbacks.

6. What is a controlled vs. an uncontrolled component?

A controlled component's value is driven by React state — every keystroke calls onChange which updates state which re-renders the input. An uncontrolled component stores its own value in the DOM and is read via a ref. Controlled components are more predictable; uncontrolled components are simpler for file inputs.

7. What are keys in React lists and why are they required?

Keys help React identify which items in a list changed, were added, or were removed during reconciliation. They must be stable and unique among siblings. Using the array index as a key is a common mistake — it breaks reconciliation when items are reordered or deleted. Use a stable database ID or a stable slug instead.

8. What is the difference between a presentational and a container component?

Presentational (or "dumb") components receive props and render UI — no side effects, no state in most cases. Container (or "smart") components manage state, fetch data, and pass it down. This pattern predates hooks; with hooks the same concerns apply even without the explicit split.


Hooks (useState, useEffect, useMemo, useRef…)

9. What are hooks and what problem do they solve?

Hooks (introduced in React 16.8) let function components use state, side effects, and other React features without writing a class. They also make it easy to extract and reuse stateful logic across components via custom hooks — something class components couldn't do cleanly.

10. What is useState?

useState(initialValue) returns [value, setter]. Calling the setter schedules a re-render with the new value. If the new state depends on the previous state, use the functional form: setCount(prev => prev + 1) — this avoids stale-closure bugs.

11. What is useEffect and when does it run?

useEffect(fn, deps) runs fn after the browser paints. With [] it runs once after mount; with values in deps it runs when any dep changes; with no second arg it runs after every render. Return a cleanup function from fn to cancel subscriptions, timers, etc., on unmount.

useEffect(() => {
  const id = setInterval(() => setTime(Date.now()), 1000);
  return () => clearInterval(id); // cleanup
}, []);

12. What are the Rules of Hooks?

Two rules enforced by ESLint (eslint-plugin-react-hooks): (1) Only call hooks at the top level — never inside loops, conditions, or nested functions, so React can maintain a consistent call order across renders. (2) Only call hooks from React functions — function components or custom hooks, never plain JavaScript functions.

13. What is useRef and when would you use it?

useRef(initial) returns a mutable object { current: initial } that persists across renders without triggering re-renders when changed. Common uses: holding a DOM reference (ref={inputRef}), storing a mutable value like a timer ID, or keeping track of the previous value of a prop.

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

useMemo(() => compute(a, b), [a, b]) memoizes an expensive computed value — it recomputes only when a or b changes. Use it when the computation is genuinely costly (not just a simple filter). Over-using useMemo adds overhead and makes code harder to read; profile first.

15. What is the difference between useMemo and useCallback?

useMemo memoizes a value. useCallback(fn, deps) memoizes a function reference — it's equivalent to useMemo(() => fn, deps). useCallback is most useful when passing a stable function reference to a child wrapped in React.memo, preventing unnecessary re-renders.

16. What is useContext and when should you use it?

useContext(MyContext) reads a context value without prop drilling. It's good for truly global data — theme, locale, auth user. Avoid putting frequently-changing values (like a counter) into context: every consumer re-renders on change. For complex state, consider a state management library instead.

17. What is useReducer and when should you prefer it over useState?

useReducer(reducer, initialState) manages state with a reducer function (like Redux). Prefer it when state has multiple sub-values that change together, when the next state depends on the previous in complex ways, or when you want the state-update logic separated from the component.


Rendering & reconciliation

18. What is reconciliation?

Reconciliation is React's algorithm for diffing the old and new virtual DOM trees to determine the minimal set of real DOM operations needed. React uses heuristics: elements of different types unmount/remount; same-type elements update in place; keys identify list items.

19. What triggers a re-render in React?

A component re-renders when: (1) its own state changes, (2) its parent re-renders (unless memoized), or (3) a context it consumes changes. React.memo wraps a component to skip re-render if its props are shallowly equal.

20. What is React.memo?

React.memo(Component) wraps a function component in a memoization layer. If the parent re-renders but the component's props haven't changed (shallow comparison), React skips re-rendering that component. Pair it with useCallback for stable function props.

21. What is React.StrictMode?

<React.StrictMode> is a development-only tool that double-invokes renders and effects to surface side effects that depend on things they shouldn't. It helps find bugs before they hit production. It has no effect in production builds.

22. What is the difference between useLayoutEffect and useEffect?

useEffect runs asynchronously after the browser paints. useLayoutEffect runs synchronously after DOM mutations but before the browser paints — use it when you need to read layout (e.g., measure an element) and then synchronously update to avoid a visual flash. Prefer useEffect unless you have a specific layout need.

23. What is concurrent rendering?

Introduced in React 18, concurrent rendering allows React to interrupt, pause, and resume renders. This keeps the UI responsive during expensive updates. Features like useTransition and useDeferredValue let you mark updates as non-urgent, so React prioritizes interactive work.


State & data flow

24. How do you share state between sibling components?

Lift state up to the closest common ancestor and pass it down as props. For deeply nested trees or cross-cutting state (like auth or theme), use Context or a state management library (Zustand, Jotai, Redux Toolkit).

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

Prop drilling is passing props through many intermediate components that don't use them, just to reach a deeply nested child. Solutions: React Context, component composition (children / render props), or a global state library.

26. What is the Context API and what are its performance trade-offs?

The Context API provides a way to pass data through the component tree without prop drilling. Trade-off: every component that calls useContext(MyCtx) re-renders when the context value changes — even if it only used a small slice. Mitigation: split contexts by update frequency, or use a selector-based library.

27. What is useTransition?

useTransition() returns [isPending, startTransition]. Wrapping a state update in startTransition tells React it's non-urgent (e.g., filtering a large list). React renders the urgent update first, keeping the UI responsive, then applies the transition. isPending lets you show a loading indicator.


Performance

28. How do you prevent unnecessary re-renders?

  • Use React.memo on pure components that receive the same props often.
  • Use useCallback to stabilize function props passed to memoized children.
  • Use useMemo for expensive derived values.
  • Keep state as local as possible — state at the top causes the whole tree to re-render.
  • Split context by concern.

29. What is code splitting and how does React support it?

Code splitting breaks the JavaScript bundle into smaller chunks loaded on demand. React supports it with React.lazy(() => import('./Component')) combined with <Suspense fallback={...}>. Next.js does route-level splitting automatically.

30. What is Suspense and what is it used for?

<Suspense fallback={<Loading />}> catches async rendering — it shows the fallback while a lazily-loaded component or a data-fetching library (React Query, Relay) signals that data is pending. In React 18+ it integrates with use() for data fetching.

31. How do you measure React performance?

Use the React DevTools Profiler tab to record renders and identify which components re-render too often. Use the browser's Performance tab for paint timing. Avoid premature optimization — profile first, then fix real bottlenecks.

32. What is virtualization and when should you use it?

Virtualization (windowing) renders only the items visible in the viewport, not the entire list. Libraries like react-window or @tanstack/react-virtual make this easy. Use it when rendering hundreds or thousands of list items that cause scroll lag.


Advanced & patterns

33. What are higher-order components (HOCs)?

An HOC is a function that takes a component and returns a new, enhanced component. Before hooks, HOCs were the primary way to share logic (withAuth, withTheme). Today, custom hooks are usually cleaner, but HOCs still appear in older codebases and libraries.

34. What is the render props pattern?

A component with a render prop accepts a function as a prop and calls it to render — sharing logic while giving the consumer control over rendering. Like HOCs, render props have been largely superseded by custom hooks.

35. What is a custom hook?

A custom hook is a JavaScript function whose name starts with use and that calls other hooks. It extracts stateful logic (e.g., useFetch, useLocalStorage) for reuse across components without changing component hierarchy.

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handler = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handler);
    return () => window.removeEventListener("resize", handler);
  }, []);
  return width;
}

36. What is the difference between createPortal and normal rendering?

ReactDOM.createPortal(children, domNode) renders children into a different DOM node than the component's parent — useful for modals, tooltips, and dropdowns that need to escape overflow/stacking contexts. Events still bubble up through the React tree as normal.

37. What are error boundaries?

Error boundaries are class components that implement componentDidCatch and getDerivedStateFromError. They catch rendering errors in their subtree and show a fallback UI instead of crashing the whole app. Function components cannot be error boundaries (yet) — use a library like react-error-boundary.

38. What is React.lazy and how does it work?

React.lazy(() => import('./MyComponent')) dynamically imports a component only when it's needed. The promise resolves to a module with a default export. It must be wrapped in <Suspense> to handle the loading state.

39. What changed in React 19?

React 19 (released late 2024) introduced the use() hook for reading promises and context inside render, Actions (async transition functions for mutations), the new <form> action integration, and improved hydration error messages. Server Components are now considered stable.


Wrap-up

These 40 questions cover the breadth of what interviewers probe in React rounds. The best preparation is writing real code: build a small app, deliberately introduce a stale-closure bug and fix it, implement a custom hook, and profile it in DevTools. That hands-on fluency is what closes the gap between knowing the answer and demonstrating it.

Practice React questions on Hiprup