Skip to content

React interview questions

React interviews test whether you can predict what renders and when, manage state and effects without bugs, and keep an app fast and accessible. Questions below are grouped by level: junior, mid and senior. Each comes with what a strong answer covers and the trap that weak answers fall into, plus code where it helps.

How Lemma tests React
React interview
Mid-level

When does an effect's cleanup run, and why does Strict Mode run effects twice in development?

What a strong answer shows

Cleanup runs before the effect re-runs with new dependencies and when the component unmounts. In development, Strict Mode mounts, unmounts and remounts each component to expose effects that do not clean up properly. The weak answer disables Strict Mode or adds a ref guard instead of writing a cleanup that undoes the setup.

Junior React interview questions

  1. 1.What causes a React component to re-render?

    What a strong answer shows

    A component re-renders when its own state changes, when its parent re-renders, or when a context it reads changes. A strong answer adds that a re-render is not a DOM update: React compares the output and commits only what changed. The weak answer is "when its props change", which misses that children re-render with their parent unless they are memoised.

  2. 2.What is the difference between state and props?

    What a strong answer shows

    Props are inputs passed down by the parent and are read-only inside the component. State is data the component owns and changes through its setter, which schedules a re-render. A strong answer explains how one component's state becomes another's props, and avoids copying props into state without a reason.

  3. 3.What are the rules of hooks, and why do they exist?

    What a strong answer shows

    Call hooks only at the top level of components and custom hooks, since React matches hooks to state by call order. A hook inside a condition or loop shifts every later hook onto the wrong state. Reciting the rules without that reason is the weak answer, and the use API is the one exception allowed inside conditions.

  4. 4.Why does React need keys in lists, and what goes wrong with array indexes as keys?

    // Bug: state follows the position, not the todo
    {todos.map((todo, i) => <TodoRow key={i} todo={todo} />)}
    
    // Fix: a stable id from the data
    {todos.map((todo) => <TodoRow key={todo.id} todo={todo} />)}

    What a strong answer shows

    Keys tell React which item is which between renders, so it can keep the right state and DOM node with each item. Index keys break when items are inserted, removed or reordered, because state stays with the position. A strong answer uses a stable id from the data and notes that index keys are fine only for static lists.

  5. 5.Why does this counter go up by one instead of three?

    const [count, setCount] = useState(0);
    
    function addThree() {
      setCount(count + 1);
      setCount(count + 1);
      setCount(count + 1); // count is 0 in every call: result is 1
    }

    What a strong answer shows

    State is a snapshot for each render, so count is 0 in all three calls and React queues the same value three times. The fix is the updater form, setCount((c) => c + 1), which receives the latest queued value. A strong answer also says that logging state right after setting it shows the old value for the same reason.

  6. 6.Why doesn't the screen update when you push to an array held in state?

    What a strong answer shows

    React compares the old and new state with Object.is, so a mutated array is the same reference and React may skip the render. A strong answer creates a new array or object with spread, map or filter, then passes it to the setter. The weak answer calls the setter with the mutated array and wonders why memoised children still show stale data.

  7. 7.What is the difference between controlled and uncontrolled inputs?

    What a strong answer shows

    A controlled input takes its value from state and reports changes through onChange, so React is the single source of truth. An uncontrolled input keeps its own value in the DOM, read through a ref or from FormData on submit. A strong answer picks controlled when the UI reacts to each keystroke and uncontrolled for simple forms.

  8. 8.What does the dependency array of useEffect control?

    What a strong answer shows

    With no array the effect runs after every render, with [] only after mount, and with values whenever one changes. A strong answer says every reactive value the effect reads belongs in the array, and that the lint rule should be fixed, not silenced. Omitting a dependency to stop a loop is the classic weak answer.

  9. 9.Why does this component render a stray 0 on the page?

    // Renders "0" when the list is empty
    {items.length && <List items={items} />}
    
    // Renders nothing when the list is empty
    {items.length > 0 && <List items={items} />}

    What a strong answer shows

    The && operator returns its left side when that side is falsy, and React renders the number 0 as text. A strong answer converts the condition to a boolean, for example items.length > 0, or uses a ternary. It shows the candidate knows JSX is plain JavaScript expressions, not a template language with its own rules.

  10. 10.Two sibling components need the same data. Where should the state live?

    What a strong answer shows

    Move the state to their closest common parent and pass it down as props, with a callback for changes. A strong answer explains that this keeps one source of truth instead of two copies drifting apart. It also recognises when the chain of props gets long enough to consider composition or context instead.

Mid-level React interview questions

  1. 11.When does an effect's cleanup run, and why does Strict Mode run effects twice in development?

    useEffect(() => {
      const conn = createConnection(roomId);
      conn.connect();
      return () => conn.disconnect(); // before the next run and on unmount
    }, [roomId]);

    What a strong answer shows

    Cleanup runs before the effect re-runs with new dependencies and when the component unmounts. In development, Strict Mode mounts, unmounts and remounts each component to expose effects that do not clean up properly. The weak answer disables Strict Mode or adds a ref guard instead of writing a cleanup that undoes the setup.

  2. 12.When should you not use useEffect?

    What a strong answer shows

    Effects are for synchronising with systems outside React, such as subscriptions, timers or the DOM. Values derived from props or state should be calculated during render, and logic caused by a user action belongs in the event handler. A strong answer spots the pattern of an effect that sets state from other state, which costs an extra render and invites bugs.

  3. 13.A profile page fetches data in an effect when userId changes. What can go wrong, and how do you fix it?

    useEffect(() => {
      const controller = new AbortController();
      fetch("/api/users/" + userId, { signal: controller.signal })
        .then((res) => res.json())
        .then(setUser)
        .catch((err) => {
          if (err.name !== "AbortError") setError(err);
        });
      return () => controller.abort();
    }, [userId]);

    What a strong answer shows

    If userId changes quickly, an older request can resolve last and overwrite the newer data. The fix is a cleanup that aborts the request or sets an ignore flag, so stale responses are dropped. A strong answer adds that a data library or framework loader handles caching, deduplication and races for you.

  4. 14.Why does this interval stop at 1?

    useEffect(() => {
      const id = setInterval(() => {
        setCount(count + 1); // count is 0 in this closure forever
      }, 1000);
      return () => clearInterval(id);
    }, []);

    What a strong answer shows

    The effect runs once, so its callback closes over the count from the first render, which is always 0. A strong answer fixes it with the updater form, setCount((c) => c + 1), which removes the need to read count at all. Adding count to the dependencies also works but recreates the interval each tick, and a strong candidate says so.

  5. 15.When would you use context instead of passing props, and when is context the wrong tool?

    What a strong answer shows

    Context suits values many distant components read and that change rarely, such as theme, locale or the current user. Every consumer re-renders when the provider's value changes, so a large, frequently changing object in one context causes wide re-renders. A strong answer splits contexts, keeps the value stable, and moves fast-changing shared state to a store with selectors.

  6. 16.What do memo, useMemo and useCallback do, and when should you not use them?

    const Row = memo(function Row({ item, onSelect }: RowProps) {
      return <li onClick={() => onSelect(item.id)}>{item.name}</li>;
    });
    
    function List({ items }: { items: Item[] }) {
      const [selected, setSelected] = useState<string | null>(null);
      // Without useCallback, a new function each render defeats memo on Row
      const onSelect = useCallback((id: string) => setSelected(id), []);
      return items.map((item) => <Row key={item.id} item={item} onSelect={onSelect} />);
    }

    What a strong answer shows

    memo skips re-rendering a component when its props are shallowly equal; useMemo caches a computed value; useCallback keeps a function's identity stable. They pay off only when profiling shows a real cost, or when a stable identity feeds a memoised child or an effect dependency. A strong answer notes that the React Compiler, where a project uses it, memoises automatically, so hand-written memoisation is needed less often.

  7. 17.What is useRef for, and how is it different from state?

    What a strong answer shows

    Unlike state, a ref holds a mutable value that survives renders without triggering one. That suits DOM nodes, timer ids and anything not shown on screen. A strong answer avoids ref.current during render, and knows React 19 passes ref to function components as an ordinary prop.

  8. 18.What is a custom hook, and what does it share between components?

    What a strong answer shows

    A custom hook is a function starting with use that calls other hooks, so stateful logic can be reused. It shares logic, not state: each component that calls it gets its own independent state. A strong answer gives a real example, such as useDebouncedValue or useOnlineStatus, and keeps the hook focused on one concern.

  9. 19.How do you handle form submission, pending state and errors in React 19?

    What a strong answer shows

    React 19 lets a form's action prop take a function, and useActionState returns the latest result, a wrapped action and an isPending flag. useFormStatus lets a nested submit button read the pending state without prop drilling, and useOptimistic shows the expected result before the server confirms. A strong answer still validates on the server and knows when a controlled form with a validation library is the better fit.

  10. 20.What does an error boundary catch, and what does it miss?

    What a strong answer shows

    An error boundary catches errors thrown while rendering its children and shows a fallback instead of unmounting the whole tree. It does not catch errors in event handlers, in async code such as a rejected fetch outside rendering, or in the boundary itself. A strong answer knows boundaries are still class components and places them around independent sections, not only at the root.

  11. 21.How do you test a React component?

    test("shows an error for an invalid email", async () => {
      const user = userEvent.setup();
      render(<SignupForm />);
      await user.type(screen.getByRole("textbox", { name: /email/i }), "nope");
      await user.click(screen.getByRole("button", { name: /sign up/i }));
      expect(await screen.findByRole("alert")).toHaveTextContent(/valid email/i);
    });

    What a strong answer shows

    Render it with React Testing Library, find elements by role and accessible name, and drive it with user-event as a person would. Assert on what the user sees, and mock the network at its boundary rather than mocking hooks or child components. The weak answer tests internal state or snapshot output, which breaks on refactors without catching real bugs.

  12. 22.How do you make a custom modal or dropdown accessible?

    What a strong answer shows

    Start with native elements: a button for actions, dialog for modals, select where it fits. For custom widgets, follow the ARIA pattern: correct roles and states, full keyboard support, and focus moved in on open and back on close. A strong answer tests with a keyboard and a screen reader, rather than patching a clickable div with ARIA.

Senior React interview questions

  1. 23.What is the difference between Server Components and Client Components, and how do you decide where the boundary goes?

    What a strong answer shows

    Server Components run only on the server, can await data directly and ship no code to the browser, but cannot use state or effects. Client Components, marked by "use client", still render on the server for the first HTML, then hydrate in the browser. A strong answer keeps client leaves small, passes server content in as children, and knows props crossing the boundary must be serialisable.

  2. 24.How does Suspense work for data loading, and what does it need from your data layer?

    What a strong answer shows

    A component suspends when it reads data that is not ready, and the nearest Suspense boundary shows its fallback until it resolves. That needs a Suspense-aware source, such as use() on a cached promise or a framework loader; fetching in an effect never suspends. A strong answer places boundaries to avoid both full-page spinners and popcorn loading, and explains how streaming sends each boundary's HTML when ready.

  3. 25.What problems do useTransition and useDeferredValue solve?

    What a strong answer shows

    Both mark work as non-urgent, so React can interrupt a slow render to keep input responsive. useTransition wraps a state update you control; useDeferredValue gives a lagging copy of a value you receive, such as a search query. Transitions also keep revealed content visible instead of a Suspense fallback, though neither makes the slow render faster.

  4. 26.A screen feels slow. How do you find and fix the cause?

    What a strong answer shows

    Measure first: the React DevTools Profiler shows which components render and why, and the Performance panel shows long tasks. Common causes are a wide context, unstable props defeating memo, unvirtualised long lists and expensive work during render. The weak answer adds useMemo everywhere before measuring, which adds cost and hides the real problem.

  5. 27.Why does this input lose its value and focus whenever Page re-renders?

    function Page() {
      // A new component type on every render of Page
      function Field() {
        const [text, setText] = useState("");
        return <input value={text} onChange={(e) => setText(e.target.value)} />;
      }
      return <Field />;
    }

    What a strong answer shows

    Field is declared inside Page, so each render creates a new component type and React unmounts the old one with its state. React keeps state by the component's type and position in the tree. A strong answer moves Field to module scope, and knows the deliberate version of this effect: changing a key to reset a component's state.

  6. 28.What causes hydration mismatch errors, and how do you fix them?

    What a strong answer shows

    The client's first render must produce the same output as the server HTML. Mismatches come from dates and time zones, random values, reading window or localStorage during render, invalid HTML nesting and browser extensions. A strong answer moves client-only values into an effect, uses useId for stable ids, and keeps suppressHydrationWarning for rare single elements.

  7. 29.How do you decide where each piece of state lives in a large React app?

    What a strong answer shows

    Server state goes in a data library or framework cache, shareable state goes in the URL, and the rest is UI state. A strong answer keeps UI state as close as possible to where it is used and lifts it only when needed. It weighs a global store's boilerplate and re-render cost, and knows useSyncExternalStore connects external stores safely to concurrent rendering.

  8. 30.How would you design the API of a reusable component, such as Tabs, for a shared library?

    What a strong answer shows

    Prefer composition over a growing list of props, for example compound components sharing state through context. Support both controlled and uncontrolled use, with a value, onChange and a default value, and forward refs and extra props to the underlying element. A strong answer builds keyboard support and ARIA in from the start and keeps the API small enough to stay stable across versions.

Prove it instead of claiming it.

Answers show you can talk about React. A dated test result and live sessions on a Skills Passport show you can do it. Lemma is in early access.

React interviews: common questions

How do I prepare for a live React coding round?

Practise building small components from a blank file: a search box with debounced fetching, a form with validation, a sortable list. Say out loud where state lives and why each effect needs its cleanup. Interviewers grade your reasoning as well as the result.

Do React interviews still ask about class components?

Rarely as a main topic. You should be able to read lifecycle methods in older code and explain how they map to effects. Error boundaries are the one place you still write a class, unless a small library wraps it for you.

How can I check my React level before applying for jobs?

Answer the questions on this page out loud and note where your answer turns vague. Lemma's adaptive React test asks 15 to 25 questions that adjust to your answers and records a dated level. A small project you can walk through in detail is the other half of the preparation.