Jump to section

React interview questions test your understanding of component architecture, state management, rendering behavior, and performance optimization. Interviewers evaluate whether you can build maintainable UIs, debug rendering issues, and make informed decisions about hooks, context, and third-party libraries in production applications.

Question Difficulty by Experience Level

LevelTopics CoveredExpected Depth
Junior (0-2 yrs) JSX, props, state, basic hooks, event handling Can build components and explain the basics
Mid (2-5 yrs) Custom hooks, context, performance, testing, patterns Can architect features and debug complex issues
Senior (5+ yrs) Architecture, state management at scale, SSR, migration Can design systems and mentor others

This guide covers:

Conceptual React Questions

These questions test whether you understand how React works under the hood. Interviewers are not looking for memorized definitions. They want to hear you reason through concepts and explain trade-offs.

1. Explain the virtual DOM and how React uses it

What interviewers assess: Foundational understanding of React's rendering model.

React creates an in-memory representation of the DOM called the virtual DOM. When state changes, React builds a new virtual DOM tree, diffs it against the previous one, and calculates the minimal set of changes needed. Those changes are batched and applied to the real DOM in a single update.

The key insight is that DOM manipulation is expensive, and batching updates reduces layout thrashing. React's fiber architecture takes this further by breaking rendering work into units that can be paused, prioritized, and resumed. This enables features like concurrent rendering and transitions.

Common mistake: Saying the virtual DOM is "faster than the real DOM." It is not inherently faster. The virtual DOM is a programming model that makes it easier to write declarative UI code while React handles efficient updates for you. The performance benefit comes from avoiding unnecessary DOM operations, not from the virtual DOM being fast on its own.

2. What are the rules of hooks?

What interviewers assess: Correctness and understanding of React's execution model.

There are two rules. First, hooks must be called at the top level of your component or custom hook. Never call them inside conditions, loops, or nested functions. Second, hooks can only be called from React function components or custom hooks, not from regular JavaScript functions.

The reason behind the first rule is that React tracks hook state by call order. Each render, React expects the same hooks to be called in the same sequence. If you put a hook inside a condition, the call order changes between renders, and React loses track of which state belongs to which hook.

Explain why the constraint exists, not just the rule itself. Interviewers want to know you understand the execution model, not that you memorized a list.

3. How does React handle reconciliation?

What interviewers assess: Understanding of the diffing algorithm and its performance implications.

Reconciliation is the process React uses to determine what changed between two renders. React compares elements by type first. If the type changes (for example, from a <div> to a <span>), React tears down the old tree and builds a new one. If the type stays the same, React updates the existing node's props.

For lists, React uses keys to match elements between renders. Without keys (or with index-as-key), React may re-render the entire list unnecessarily or produce bugs when items are reordered. Stable, unique keys allow React to efficiently insert, remove, and move items.

The diffing algorithm is O(n) by design. React makes the simplifying assumption that elements of different types produce different trees. This is nearly always true in practice and keeps the algorithm fast.

4. When would you use useRef vs useState?

What interviewers assess: Mental model of React's render cycle.

useState stores values that affect what the user sees. When you update state, React re-renders the component. useRef stores values that persist across renders but do not trigger re-renders when they change.

Use useRef for DOM element references, storing timer IDs, tracking previous values, and holding any mutable data that does not affect visual output. Use useState for anything the user sees or interacts with: form inputs, toggle states, counters, loading flags.

A common interview follow-up is: "What happens if you store a value in useRef and try to display it?" The answer is that the display will not update when the ref changes because React does not know about it. Only state changes trigger re-renders.

5. Explain React's context API and its limitations

What interviewers assess: State management decisions and awareness of trade-offs.

Context provides a way to pass data through the component tree without threading props through every level. You create a context with createContext, provide a value at the top with a Provider component, and consume it anywhere below with useContext.

The main limitation is performance. Any component that calls useContext re-renders whenever the context value changes, even if the component only uses a small part of that value. There is no built-in selector mechanism like you get with Redux or Zustand.

Context works well for low-frequency updates: themes, locale, authentication status. For high-frequency updates like form state or real-time data, external state management libraries are usually a better choice. Discuss the trade-off: context is simple and built-in, but it can cause unnecessary re-renders at scale.

6. What is the difference between controlled and uncontrolled components?

What interviewers assess: Form handling patterns and when to use each.

In a controlled component, React manages the input's value through state. Every keystroke updates state, and the input's value is always driven by that state. In an uncontrolled component, the DOM itself holds the current value, and you access it through a ref when needed.

Controlled components give you real-time access to the input value, which enables validation on every keystroke, conditional rendering based on input, and coordinated multi-field forms. The trade-off is more boilerplate: you need a state variable and an onChange handler for each input.

Uncontrolled components are simpler for basic forms where you only need the value on submit. File inputs are always uncontrolled because their value is read-only in the browser.

7. How does React handle error boundaries?

What interviewers assess: Production readiness and resilience patterns.

Error boundaries are class components that implement componentDidCatch and getDerivedStateFromError. They catch JavaScript errors during rendering, in lifecycle methods, and in constructors of child components. When an error is caught, the boundary renders a fallback UI instead of crashing the entire app.

Important limitations: error boundaries do not catch errors in event handlers, asynchronous code (setTimeout, promises), or server-side rendering. For event handler errors, you need regular try/catch blocks.

In production, you typically place error boundaries at route level and around critical features. The fallback UI should give users a way to recover, like a "Try again" button or navigation to a working page. Pair error boundaries with error reporting services to capture and triage issues.

8. Explain server-side rendering and when you would use it

What interviewers assess: Architecture decisions and framework knowledge.

Server-side rendering (SSR) runs your React code on the server and sends fully rendered HTML to the client. The browser displays the HTML immediately while JavaScript loads in the background. Once JavaScript is ready, React "hydrates" the page, attaching event listeners to the existing DOM.

SSR improves initial load time and is essential for SEO on content-heavy pages. Streaming SSR (introduced in React 18) sends HTML progressively, so users see content before the entire page is rendered.

The trade-offs: SSR adds server complexity, increases time-to-first-byte if your data fetching is slow, and requires careful handling of browser-only APIs. Static site generation (SSG) is better for content that does not change per request. Client-side rendering is fine for authenticated dashboards where SEO does not matter.

Frameworks like Next.js handle SSR, SSG, and hybrid rendering out of the box, which is why they are increasingly expected in full-stack React roles.

Ready to put this into practice?

Practice this with MockIF →

Practical React Questions

Practical questions test whether you can build things, not just explain concepts. Interviewers want to see clean code, awareness of edge cases, and thoughtful decisions about when to use built-in tools vs libraries.

1. Build a debounced search input

What interviewers assess: Hooks composition and cleanup logic.

Create a custom hook that wraps useState and useEffect with a debounce timer. The hook accepts a value and a delay, then returns the debounced value. Inside the effect, set a timeout that updates the debounced value after the delay. Return a cleanup function that clears the timeout.

The cleanup function is critical. Without it, you get stale closures: if the component unmounts before the timeout fires, you will try to update state on an unmounted component. The cleanup runs before every re-execution of the effect and on unmount.

Edge cases to discuss: race conditions when async search results return out of order (the solution is an AbortController or a flag that ignores stale results), and what happens if the user types and then navigates away mid-debounce. A strong answer separates the debounce logic into a reusable hook and the search logic into the consuming component.

2. Implement an infinite scroll component

What interviewers assess: Performance awareness and API usage.

Use the IntersectionObserver API to detect when a sentinel element near the bottom of the list enters the viewport. When it does, trigger the next page fetch. This is more efficient than scroll event listeners because IntersectionObserver runs off the main thread.

Handle loading states (show a spinner while fetching), error states (show a retry button if a request fails), and deduplication (prevent firing multiple requests for the same page). Track whether there are more pages to load and remove the observer when the list is complete.

For very long lists (thousands of items), discuss virtualization. Libraries like react-window or react-virtuoso render only the visible items plus a small buffer, keeping the DOM size constant regardless of list length. This prevents memory issues and keeps scrolling smooth.

3. Your component re-renders too often. How do you debug it?

What interviewers assess: Performance debugging methodology.

Start with React DevTools Profiler. Record an interaction and look at which components re-rendered and why. Common culprits: a parent re-rendering causes all children to re-render, new object or function references created on every render get passed as props, or context changes trigger updates in components that do not need them.

Apply fixes based on the root cause. React.memo prevents a child from re-rendering when its props have not changed. useMemo stabilizes object and array references. useCallback stabilizes function references. But only apply these after profiling. Premature memoization adds complexity without solving the actual problem.

Common mistake: Wrapping everything in React.memo and useMemo without measuring. This adds memory overhead and makes code harder to read. Find the bottleneck first, then optimize surgically.

4. Create a custom hook for API data fetching

What interviewers assess: Abstraction skills and error handling.

Build a useFetch hook that manages three states: loading, data, and error. Accept a URL and optional configuration. Use useEffect to trigger the fetch when the URL changes. Handle the response, parse JSON, and update the appropriate state.

The critical detail is handling race conditions. If the URL changes while a request is in flight, the old request's response should be ignored. Use an AbortController to cancel the previous request, or use a boolean flag in the effect cleanup to mark stale responses.

Discuss when you would stop using a custom hook and reach for a library like React Query or SWR. These libraries handle caching, deduplication, background refetching, retry logic, and optimistic updates. For a production application with many API calls, they save significant development time and cover edge cases that are hard to get right manually.

5. Implement a modal system that supports stacking

What interviewers assess: Component composition, portal usage, and accessibility.

Use createPortal to render modals outside the component tree, directly into document.body or a dedicated modal root. This avoids z-index conflicts and overflow:hidden issues from parent containers.

For stacking, maintain an array of active modals in state. Each modal gets a z-index based on its position in the stack. Pressing Escape closes the topmost modal only. Clicking the backdrop closes the topmost modal only.

Accessibility requirements: trap focus within the active modal (Tab and Shift+Tab should cycle through focusable elements inside it), restore focus to the trigger element when the modal closes, and add role="dialog" and aria-modal="true" attributes. Discuss how you would handle screen readers and keyboard navigation throughout.

6. Refactor a class component to use hooks

What interviewers assess: Migration knowledge and lifecycle understanding.

Map lifecycle methods to hooks systematically. componentDidMount becomes useEffect with an empty dependency array. componentDidUpdate becomes useEffect with specific dependencies. componentWillUnmount becomes the cleanup function returned from useEffect.

Replace this.state with individual useState calls. If the class had many related state values, you can use useReducer instead. Replace this.context with useContext.

Watch for subtle differences. Class components batch state updates only inside event handlers, while function components with React 18+ batch all state updates automatically. Also, useEffect runs after paint, while componentDidMount runs before paint. If you need the pre-paint timing, use useLayoutEffect instead.

System Design with React

Senior React interviews often include system design questions. You will not be asked to draw boxes and arrows for backend infrastructure. Instead, you will discuss how to structure a large frontend application, manage state across features, and make performance trade-offs.

State Management at Scale

Start with the simplest approach that works. Local component state (useState) handles most UI state: form inputs, toggle visibility, local loading flags. Context is appropriate for global, low-frequency data like authentication, theme, and locale.

When you need shared state that updates frequently or is accessed by many components, reach for an external library. Redux is well-established but verbose. Zustand offers a simpler API with similar capabilities. Jotai uses an atomic model where each piece of state is independent. The right choice depends on your team's experience and the complexity of your state graph.

The key principle is colocating state close to where it is used. Do not hoist state to the top of the tree "just in case." Every piece of state lifted higher than necessary causes unnecessary re-renders in components below. If two sibling components share state, lift it to their nearest common parent and no further.

Component Architecture

Atomic design is a common framework: atoms (buttons, inputs), molecules (search bars, form fields), organisms (navigation, data tables), templates (page layouts), and pages. This gives teams a shared vocabulary and makes component reuse predictable.

Compound components (like a <Tabs> component with <Tab> and <TabPanel> children) provide flexible APIs without prop explosion. The parent manages shared state and passes it down through context or cloneElement.

Hooks have largely replaced render props and higher-order components for sharing logic. Custom hooks are easier to compose, test, and understand. Use them to extract reusable behavior like data fetching, form validation, keyboard shortcuts, and animation state.

For large codebases, organize by feature rather than by type. Group all files for a feature (components, hooks, tests, styles) in one directory instead of spreading them across separate component/, hook/, and style/ folders. This makes it easier to find, modify, and delete features as requirements change.

Performance at Scale

Code splitting with React.lazy and Suspense reduces initial bundle size. Split at the route level first, then at the feature level for large routes. Prefetch routes the user is likely to visit next using import() on hover or viewport entry.

Image optimization matters more than most developers think. Use modern formats (WebP, AVIF), serve responsive sizes with srcset, and lazy-load images below the fold. Frameworks like Next.js handle this automatically with their Image component.

Bundle analysis with tools like webpack-bundle-analyzer or source-map-explorer reveals unexpected dependencies inflating your bundle. Common offenders: moment.js (replace with date-fns or dayjs), lodash (import individual functions), and icon libraries (tree-shake or use SVGs directly).

Testing Strategy

Unit tests for custom hooks: use renderHook from Testing Library to test hooks in isolation. Verify state changes, side effects, and cleanup behavior.

Integration tests for user flows: render full components with Testing Library and simulate user interactions. Test what the user sees and does, not implementation details. Query by role, label, and text rather than test IDs or class names.

End-to-end tests for critical paths: use Playwright or Cypress for login flows, checkout, and other business-critical journeys. Keep E2E tests focused and fast. Run them in CI but do not rely on them for comprehensive coverage.

The Testing Library philosophy is worth mentioning: "The more your tests resemble the way your software is used, the more confidence they can give you." This means testing behavior, not implementation. Avoid testing internal state or method calls directly.

Ready to put this into practice?

Practice this with MockIF →

Common React Interview Mistakes

Over-engineering simple components

Not every component needs a custom hook, context provider, and memoization. Start simple and add abstractions only when the complexity demands it. Interviewers notice when you complicate straightforward problems.

Using useEffect for everything

useEffect is not a lifecycle method replacement. It synchronizes your component with external systems. If you find yourself using useEffect to derive state from other state, you probably need a computed value or useReducer instead. The React docs call this "You Might Not Need an Effect" for a reason.

Not explaining trade-offs

When an interviewer asks "which approach would you use," they do not want a single answer. They want to hear you compare options, acknowledge downsides, and explain why one approach fits the situation better. Always discuss trade-offs.

Forgetting accessibility in component design

If you build a custom dropdown, modal, or tab component without mentioning keyboard navigation, focus management, or ARIA attributes, you are leaving points on the table. Accessibility is part of component quality, not an afterthought.

Memorizing hook signatures without understanding the mental model

Knowing that useEffect takes a dependency array is not enough. You need to understand why it exists (to control when the effect re-runs), what happens when you get it wrong (stale closures, infinite loops), and how cleanup works. Interviewers probe the "why" behind every answer.

Ignoring error handling and loading states

When you build a component in an interview, always handle the loading state, the error state, and the empty state. Jumping straight to the happy path makes your solution look incomplete. Production components deal with failure, and interviewers expect you to show that awareness.

Frequently Asked Questions

Should I learn class components for interviews?
Understand them enough to read and migrate existing code, but focus your preparation on hooks for new code. Most companies have moved to functional components, and interviewers primarily test hooks-based patterns. That said, error boundaries still require class components, so know how those work.
How important is knowing Redux for React interviews?
Less critical than it used to be. Many companies now use lighter alternatives like Zustand, Jotai, or React Query. What matters more is understanding the pattern: unidirectional data flow, actions, reducers, and selectors. If you understand the concept, you can pick up any specific library quickly. Check the job description for signals about which tools the team uses.
Do I need to know Next.js?
It depends on the role. Full-stack React positions increasingly expect Next.js experience. For frontend-only roles, you should at minimum understand SSR concepts, static generation, and hydration. If the job listing mentions Next.js, spend time building something with it. If it does not, conceptual knowledge is usually sufficient.
How do I practice React interview questions?
Build small projects that exercise specific concepts: a debounced search, a data table with sorting and pagination, a multi-step form with validation. Then practice explaining your decisions out loud. Reading code is not enough. You need to articulate your reasoning clearly under time pressure. AI mock interviews help you practice both building and explaining.
What React version should I study?
Focus on React 18+ features: concurrent rendering, automatic batching, transitions with useTransition, and Suspense for data fetching. Interviewers care about current patterns, not legacy APIs. If a company is still on React 16 or 17, understanding the upgrade path shows initiative and awareness of the ecosystem.

Stop Preparing in Your Head. Start Practicing Out Loud.

Drop your resume, add a job description, and get a mock interview like the real thing.

Start Free Mock Interview →
No credit card required. 5 free credits to start.