Skip to content
tezvyn:

Top 30 Intermediate React & Next.js Interview Questions and Answers

30 intermediate multiple-choice React & Next.js interview questions, past the definitions: how the pieces fit together, what breaks in practice, and the trade-off behind a choice. They come from 30 bites in the React & Next.js library, the middle slice of the 144 React & Next.js interview questions in the library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.

React, Next.js, Remix, RSC, React ecosystem

30 questions. Pick an answer, or open “Show the answer” to read it.

Answers are graded in your browser. Nothing is saved, and no XP or streak is earned here. The app keeps score.

  1. Question 1 of 30

    When a dynamic list reorders and items have local state, what happens if array indices are used as React keys?

    Show the answer

    Answer: d · React reuses component instances by position, causing state from one data item to appear on another.

    Array indices tie identity to position, so React reuses the wrong component instance after reorder or deletion and state ends up attached to the wrong data item. Option B is wrong because React does not reset state when keys are unstable—it incorrectly preserves it on the reused instance.

    Read the full bite: What is the purpose of React's key prop and index key risks?

  2. Question 2 of 30

    In React, when is an early return with if/else preferable to an inline ternary inside JSX?

    Show the answer

    Answer: a · When branches are large, structurally different, or to reduce nesting

    Early returns improve readability when branches are large, structurally different, or deeply nested. Option B actually describes when an inline ternary is the better choice, not an early return.

    Read the full bite: Describe two patterns for conditional rendering in JSX

  3. Question 3 of 30

    Why would wrapping two table cells in a Fragment be preferable to wrapping them in a div?

    Show the answer

    Answer: a · Fragments avoid creating an invalid HTML structure since they do not add an extra DOM node.

    A div placed directly inside a table row creates invalid HTML, while a Fragment groups children without introducing any DOM node. Claiming a performance benefit is a common misconception; the primary reason to use Fragments is structural and semantic correctness, not optimization.

    Read the full bite: What is a React Fragment and why use it over a div?

  4. Question 4 of 30

    A team passes a user object through Layout, Header, Navigation, and finally to UserMenu. What is the strongest argument for replacing this with Context or composition?

    Show the answer

    Answer: a · Layout, Header, and Navigation are coupled to a prop they ignore, making refactors and reuse harder.

    Prop drilling is primarily a maintenance and coupling problem: intermediaries must accept and forward props they do not use, so renaming the prop or reusing those components elsewhere becomes painful. Option B is tempting but wrong because the card explicitly warns against confusing drilling with runtime performance issues like excessive re-renders.

    Read the full bite: Explain prop drilling and why it's a problem

  5. Question 5 of 30

    You need to increment a React state variable three times within a single event handler. Which approach produces the correct final value?

    Show the answer

    Answer: c · Call setCount(prev => prev + 1) three times sequentially

    Option C uses the functional updater so React queues each increment against the latest pending state. Option B fails because all three calls read the same closed-over value from the render snapshot, so each computes 0 + 1.

    Read the full bite: What will count be after three setCount calls in a row?

  6. Question 6 of 30

    Which approach correctly fetches data inside useEffect while preventing memory leaks when the component unmounts?

    Show the answer

    Answer: a · Create an async function inside the effect, invoke it, and return a cleanup function that aborts the request.

    Creating an async function inside the effect allows the outer callback to remain synchronous and return an abort cleanup function. Declaring the useEffect callback itself as async is a common mistake because it returns a Promise, breaking React's cleanup contract.

    Read the full bite: How do you fetch data with useEffect and prevent memory leaks?

  7. Question 7 of 30

    A useEffect subscribes to an external store. When a dependency changes and the effect must re-run, when does the cleanup function execute?

    Show the answer

    Answer: d · Immediately before the effect re-runs with the new dependency values

    React runs the cleanup function immediately before re-executing an effect when dependencies change, tearing down the previous render's side effects to prevent memory leaks and stale subscriptions. The most tempting distractor assumes cleanup is only for unmount, but that ignores the re-run scenario and fails under React 18 Strict Mode, which intentionally double-invokes effects to expose missing teardown logic.

    Read the full bite: What is the purpose of the useEffect cleanup function?

  8. Question 8 of 30

    What is the key difference between useState(expensive()) and useState(() => expensive()) during re-renders?

    Show the answer

    Answer: a · The direct call executes on every render but React discards its result after mount, while the function runs only once during initialization.

    JavaScript evaluates the direct argument before React can intercept it, so expensive() wastes cycles on every render even though React throws away the result after mount; the initializer function runs only once. Option D is tempting but wrong because lazy initialization does not prevent re-renders, it only avoids repeating expensive setup work inside them.

    Read the full bite: Why pass a function to useState for expensive initial values?

  9. Question 9 of 30

    For which situation should you reach for useReducer instead of multiple useState hooks?

    Show the answer

    Answer: d · Coordinating several related fields that must update atomically from one action

    useReducer excels when one action must update multiple interdependent fields atomically, centralizing transition logic outside the component. Option A is a common misconception because the hook itself does not memoize child renders or improve performance without additional optimizations like React.memo.

    Read the full bite: Explain useReducer and when to prefer it over useState

  10. Question 10 of 30

    A ThemeContext.Provider wraps the entire app and passes an unmemoized object containing theme and toggleTheme. What is the main performance risk?

    Show the answer

    Answer: d · Every consumer re-renders whenever the Provider re-renders because the object reference changes each time

    React compares context values by reference, so a new object literal on every render triggers re-renders in all consumers even if the theme data is unchanged. Option A is tempting because developers often assume context supports selective subscriptions by property, but React does not optimize that way.

    Read the full bite: Use useContext to provide theme state without prop drilling

  11. Question 11 of 30

    A parent computes an expensive object and defines an inline handler passed to a React.memo child. Which hook combination correctly optimizes the computation and prevents unnecessary child re-renders?

    Show the answer

    Answer: d · useMemo for the object and useCallback for the handler

    useMemo caches the computed object value, while useCallback preserves the function reference so React.memo's Object.is check sees an unchanged prop and skips the child render. Choosing useMemo for the handler confuses the two hooks because useMemo caches the returned value rather than the function reference, and omitting a hook creates a new inline function on every parent render.

    Read the full bite: Explain the difference between useMemo and useCallback

  12. Question 12 of 30

    What fundamental difference distinguishes a custom hook from a HOC when reusing stateful logic in React?

    Show the answer

    Answer: b · A custom hook extracts logic into a plain function without adding nodes to the component tree.

    Custom hooks reuse stateful logic as plain functions without adding nodes to the React tree or hiding prop sources, whereas HOCs wrap components and can create wrapper hell. Option D is tempting because it echoes the common misconception that hooks are just syntactic sugar for HOCs, but they represent a fundamentally different composition mechanism.

    Read the full bite: Compare HOCs and custom hooks for sharing logic

  13. Question 13 of 30

    Which approach best demonstrates the idiomatic way to handle a variant prop in a styled-components Button?

    Show the answer

    Answer: c · Pass variant to a single styled.button and use an interpolated function inside the template literal to map the prop value to CSS rules

    The card describes the idiomatic pattern as using a single styled.button with an interpolated function that reads props.variant to map CSS rules, keeping the API prop-driven and the component reusable. Splitting into separate components, using inline styles, or manually concatenating classNames are all explicitly identified as red flags that defeat the purpose of the CSS-in-JS library.

    Read the full bite: How would you create a styled Button accepting a variant prop

  14. Question 14 of 30

    In this architecture, what is the primary responsibility of the custom React Context provider?

    Show the answer

    Answer: c · To hold the current mode string and toggle function while the CSS-in-JS ThemeProvider injects derived tokens into styles

    The custom Context manages state and actions such as the mode string and toggle, while the CSS-in-JS ThemeProvider is what actually injects the derived theme tokens into component styles. Option B describes the library provider's job, and option D combines prop drilling with unnecessary rebuilds, both listed as common anti-patterns.

    Read the full bite: Implement a light/dark theming system with CSS-in-JS and React Context

  15. Question 15 of 30

    How should a ProtectedRoute wrapper preserve the original destination when redirecting an unauthenticated user from /dashboard to login?

    Show the answer

    Answer: d · Pass the current location via the state prop on the Navigate component

    The card recommends passing the current location in Navigate's state so the login flow can return the user to /dashboard after authentication. The replace prop only prevents an extra history entry; it does not store the intended destination, making D a common misconception.

    Read the full bite: Implement a protected /dashboard route in React Router

  16. Question 16 of 30

    In React Router v6, how does a component rendered by /products/:productId correctly read the productId URL parameter?

    Show the answer

    Answer: d · Call useParams inside the component and read the productId property

    useParams is the idiomatic v6 hook for reading dynamic segments, returning an object with keys like productId. props.match.params is the legacy v5 approach, manual parsing bypasses the router, and omitting the colon prefix creates a static route that only matches the literal string.

    Read the full bite: How do you create a dynamic route like /products/:productId in React Router?

  17. Question 17 of 30

    When defining a dynamic UI route in Next.js, what is the fundamental difference between how Pages Router and App Router map the file system to the URL path?

    Show the answer

    Answer: d · App Router treats folders as route segments and requires page.js for the leaf UI, while Pages Router uses the JavaScript file itself as the route endpoint.

    App Router uses folders as route segments and requires page.js for leaf UI, while Pages Router uses JavaScript files directly as route endpoints. Option C is tempting but wrong because App Router is not merely a folder rename; it introduces co-located segment-level primitives like layout.js and loading.js that Pages Router lacks.

    Read the full bite: Compare Pages Router and App Router file-based routing conventions and capabilities

  18. Question 18 of 30

    Which scenario most clearly justifies using useReducer instead of useState for React form state?

    Show the answer

    Answer: c · A multi-step wizard where country selection changes provinces, enables VAT, and triggers async tax lookups

    The wizard's cascading interdependencies and async transitions are best centralized in a pure reducer; option D describes coupled fields appropriately managed by a single object with useState, not useReducer.

    Read the full bite: Prefer multiple useState or useReducer for multi-field forms?

  19. Question 19 of 30

    When building real-time email validation in React, which state strategy best follows declarative, state-driven patterns?

    Show the answer

    Answer: d · Store the raw input in useState, use a single status enum such as typing or error, and derive validation during render.

    The correct approach uses a single status enum and derives validation during render, avoiding redundant state. Option A is wrong because multiple boolean flags require manual synchronization and invite stale state bugs.

    Read the full bite: How would you implement real-time client-side validation in React?

  20. Question 20 of 30

    How does React Hook Form primarily avoid re-rendering an entire form on every keystroke, compared to manual useState management?

    Show the answer

    Answer: d · It registers inputs via refs and treats them as uncontrolled, reading values only when needed.

    React Hook Form attaches refs to inputs and keeps them uncontrolled so React does not render on every keystroke; values are read at validation or submission. The first option describes Formik's context-and-subscription strategy, which is a common point of confusion between the two libraries.

    Read the full bite: Advantages of dedicated React form libraries over manual state

  21. Question 21 of 30

    You are building a Next.js newsletter form that must work without JavaScript and show server validation errors. Which pattern correctly wires the Server Action to the form?

    Show the answer

    Answer: b · Pass the Server Action to useFormState, place the bound action on the form's action prop, and read validation errors from the returned state

    useFormState is designed for progressive enhancement by binding the returned action directly to the form's action attribute and surfacing server-returned state, whereas calling the action in onSubmit with useState breaks JavaScript-free submission and defeats the purpose of the hook.

    Read the full bite: How do you use a Server Action for form submission and useFormState?

  22. Question 22 of 30

    A long-lived list component wraps a cheap per-item string format in useMemo. What net performance impact is most likely?

    Show the answer

    Answer: b · The aggregate cost of dependency comparisons and array allocations exceeds the cost of recomputing the format inline.

    For cheap computations, the per-render cost of dependency checks and array allocations across many items typically exceeds recomputation, and cached values persist for the component's lifetime. Option A is tempting but wrong because useMemo alone does not prevent child re-renders unless the child is wrapped in React.memo.

    Read the full bite: When can overusing useMemo hurt performance and what are the trade-offs?

  23. Question 23 of 30

    Which statement accurately describes the runtime behavior when a React.lazy component is first rendered within a Suspense boundary?

    Show the answer

    Answer: c · React invokes the loader on first render, caches the result, and Suspense displays a fallback until the Promise resolves.

    React calls the lazy loader only on first render and caches both the Promise and resolved module, while Suspense catches the thrown Promise to show a fallback. Distractor A is wrong because lazy does not preload during module initialization; loading is deferred until the component is first rendered.

    Read the full bite: How do React.lazy and dynamic import() enable code splitting?

  24. Question 24 of 30

    What mechanism allows Next.js to split routes into separate chunks without developers using React.lazy?

    Show the answer

    Answer: b · The file-system routing convention treats each page.js as an entry point, producing chunks at build time.

    Next.js uses its file-system routing convention to make each page.js a discrete bundler entry point, automatically emitting separate chunks at build time. It does not rely on React.lazy under the hood, and prefetching is only an optimization that loads chunks already created by the routing contract.

    Read the full bite: How does Next.js routing auto-implement code splitting?

  25. Question 25 of 30

    A Profiler reports actualDuration 80 ms and baseDuration 78 ms during an update. What does this suggest?

    Show the answer

    Answer: b · Memoization is failing and descendants are re-rendering unnecessarily

    When actualDuration is nearly equal to baseDuration during an update, React is not skipping any descendants via memoization, so adding memo or useMemo should drop actualDuration while baseDuration remains high. Option C would be correct if baseDuration were high but actualDuration were low, indicating an inherently expensive subtree that is already being memoized.

    Read the full bite: React Profiler long render duration: causes and next steps?

  26. Question 26 of 30

    A component only displays state.dashboard.theme, but its useSelector returns the entire dashboard object. It re-renders when unrelated dashboard data changes. What is the best fix?

    Show the answer

    Answer: a · Return state.dashboard.theme from the selector instead of the whole dashboard object

    Returning the primitive theme allows strict equality checks to succeed when unrelated dashboard data changes, preventing extra re-renders. shallowEqual is tempting but still compares the full dashboard object, so it re-renders when widgets or layout change, while React.memo and store splitting treat symptoms instead of fixing selector granularity.

    Read the full bite: How do you select a single property and prevent extra re-renders?

  27. Question 27 of 30

    What is the key architectural difference between handling async calls in Redux Toolkit versus Zustand?

    Show the answer

    Answer: a · Redux Toolkit auto-generates action lifecycles that reducers map to state, while Zustand leaves async structure up to the developer

    Redux Toolkit's createAsyncThunk automatically produces pending, fulfilled, and rejected actions for reducers to handle, whereas Zustand stores rely on manual set calls inside plain async functions without an enforced pattern. Distractor A is tempting because beginners may not realize RTK generates those lifecycles automatically rather than requiring manual dispatch after each API call.

    Read the full bite: How would you handle an async API call in Redux Toolkit versus Zustand?

  28. Question 28 of 30

    What fundamentally allows atomic state libraries to prevent unrelated component re-renders without requiring memoized selectors?

    Show the answer

    Answer: b · State is organized as a bottom-up graph where components subscribe only to the atoms they consume.

    Atomic libraries structure state as a bottom-up graph where components subscribe to specific atoms, so updates to one atom only re-render its consumers. The mini-stores distractor mischaracterizes atoms as isolated Redux slices and misses the dependency graph and granular subscription model that actually prevents extra renders.

    Read the full bite: Explain atomic state management and how it differs from Redux

  29. Question 29 of 30

    When testing a component that fetches data on mount, which approach correctly asserts the final rendered state without race conditions?

    Show the answer

    Answer: b · Mock the API, render the component, await screen.findByText for the final data, and assert the loading state with getByText if it renders synchronously.

    B is correct because mocking isolates the test and findBy retries until the async data appears, while getBy is safe for the synchronous loading state. A is tempting but wrong because getBy throws if the element has not yet appeared after the initial render.

    Read the full bite: Test a React component fetching async data on mount

  30. Question 30 of 30

    When unit testing a custom React hook like useCounter in isolation, what is the correct pattern to trigger and assert state updates?

    Show the answer

    Answer: b · Use renderHook, wrap the interaction in act, and read the updated state from result.current.

    renderHook creates the component boundary hooks require, and act flushes state updates so result.current reflects the new value. D is tempting because it uses renderHook but skips act, leading to unflushed updates and React warnings.

    Read the full bite: How do you unit test a custom React hook like useCounter?

Could you explain these out loud?

That is what an interview actually tests. Tezvyn gives you questions like these with what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon