DEV Community

Mahdar
Mahdar

Posted on

Top 30 React Interview Questions and Answers in 2025

This guide breaks down the core concepts that define the modern React ecosystem, focusing on the game-changing features in React 19 and the Server Component Architecture.


1. What are React Server Components (RSCs) and how do they work?
React Server Components (RSCs) represent a fundamental paradigm shift in how we build React applications. They allow components to be rendered exclusively on the server, shifting work out of the client-side bundle and execution environment. They are a complement to, not a replacement for, traditional Client Components.

How they work:

  • Zero-Bundle-Size: RSC code, including large dependencies like date libraries or Markdown parsers, is never shipped to the client. This drastically reduces the JavaScript bundle size.

  • Server-Only Execution: They run entirely on the server. They cannot use state (useState), effects (useEffect), or browser-only APIs (like onClick). They are primarily for data fetching and static UI composition.

  • Direct Backend Access: They can securely and directly access backend resources (databases, file systems, internal services) without the performance overhead or complexity of a separate REST/GraphQL layer.

  • Understanding React Server Components

  • Integrated Rendering: The React framework (Next.js App Router, Remix, etc.) orchestrates the rendering. It sends the RSC output as a lightweight "UI description" to the client, while reserving standard client-side JavaScript for interactive elements.

  • Composition: RSCs can import and render Client Components seamlessly, but the reverse is not true (Client Components cannot import Server Components).

Example: A ProductPage Server Component handles the secure data fetch and renders the static layout, which then imports an interactive AddToCartButton Client Component.


2. What is the React Compiler in React 19?

The React Compiler (formerly "React Forget") is a new, automatic compiler designed to fundamentally solve the problem of unnecessary re-renders in React. Its primary goal is to automatically manage memoization, making manual calls to useMemo, useCallback, and React.memo obsolete.

How it works:
The compiler deeply analyzes your component code and applies memoization only where it's truly beneficial. It ensures that components only re-render when the UI's semantic meaning has actually changed

  • Before (Manual): Developers had to correctly identify and wrap logic/values to prevent unnecessary re-creation on every render.
// Manual memoization and dependency array management
const expensiveValue = useMemo(() => calculate(a, b), [a, b]);

Enter fullscreen mode Exit fullscreen mode

After (Automatic): The compiler ensures that the variable expensiveValue retains its identity and value across renders if its dependencies (a and b) haven't changed—without any manual wrapping.

// The compiler does the memoization automatically
const expensiveValue = calculate(a, b);
Enter fullscreen mode Exit fullscreen mode

This change is critical: it leads to less boilerplate, eliminates a major source of performance bugs, and allows developers to write cleaner, more standard JavaScript. What does React Compiler do?


3. What are Actions in React 19?

Actions are a first-class feature for managing data mutations and submissions, typically triggered by a form. They are designed to integrate natively with Server Components, providing a robust, built-in solution for form handling, pending states, and optimistic updates.

Whats-new-in-react-19?

How they work:
An async function—the "Action"—is passed directly to a form element's action prop. This function can execute on the server (Server Action) or the client. React manages the entire submission lifecycle: preventing default behavior, handling loading states, and managing errors.

// A Server Component
function AddToCart({ productId }) {
  // The 'use server' directive marks this function for server-side execution.
  async function addToCartAction(formData) {
    'use server'; 
    const itemId = formData.get('itemId');
    await addToCart(itemId); // Securely mutate data on the server
  }

  return (
    <form action={addToCartAction}> {/* Pass the async function as the action */}
      <input type="hidden" name="itemId" value={productId} />
      <button type="submit">Add to Cart</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

4. What is the useActionState Hook?

The useActionState hook (previously useFormState) is the client-side counterpart to Actions. It allows a component to handle the state and response that results from an Action submission.
useActionState

"use client";

import { useActionState } from "react";
import { createUser } from "./actions";

const initialState = {
  message: "",
};

export function Signup() {
  const [state, formAction, pending] = useActionState(createUser, initialState);

  return (
    <form action={formAction}>
      <label htmlFor="email">Email</label>
      <input type="text" id="email" name="email" required />
      {/* ... */}
      {state?.message && <p aria-live="polite">{state.message}</p>}
      <button aria-disabled={pending} type="submit">
        {pending ? "Submitting..." : "Sign up"}
      </button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

5. What is useFormStatus Hook?

The useFormStatus hook is a simple way to access the submission status of the nearest parent <form> that is executing an Action.

useformstatus
How it works:

This hook provides metadata about the form submission, primarily the pending boolean, which is ideal for disabling the submit button or showing a loading spinner. Crucially, it must be used inside a Client Component that is a descendant of the <form> being processed.

'use client';
import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Submitting...' : 'Save Changes'}
    </button>
  );
}

Enter fullscreen mode Exit fullscreen mode

6. What is useOptimistic Hook?
The useOptimistic hook is dedicated to implementing optimistic UI updates. It allows the application to show the user the expected result of a mutation instantly, even before the network request has completed, significantly improving perceived performance.

How it works:

It maintains two versions of state: the real state and the optimistic state. When a mutation starts, you update the optimistic state immediately. When the server response is received (success or error), the optimistic state automatically syncs back to the real state.

import { useOptimistic } from 'react';

function MessageList({ messages }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages, // The real state (from server)
    // The function to compute the optimistic state
    (currentMessages, newMessageText) => [...currentMessages, { text: newMessageText, sending: true }]
  );

  async function formAction(formData) {
    const newMessageText = formData.get('message');
    addOptimisticMessage(newMessageText); // Optimistically update the UI
    await sendMessage(newMessageText); // The real server request (takes time)
  }

  return (
    <>
      {optimisticMessages.map((msg, index) => (
        <div key={index}>{msg.text} {msg.sending && <small>(Sending...)</small>}</div>
      ))}
      <form action={formAction}>...</form>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

7. What is Concurrent Rendering in React?

Concurrent Rendering is a foundational feature introduced in React 18 that makes the rendering process interruptible. It allows React to work on multiple states or "versions" of the UI simultaneously, pausing, resuming, or abandoning a render based on priority. more...
Key Benefit:

It prevents the main thread from being blocked by non-urgent updates, ensuring the UI remains highly responsive (e.g., typing in an input field is always smooth) even when a heavy, lower-priority update is processing in the background.

Mechanisms:

  • Time Slicing: Breaking rendering work into small chunks.
  • Suspense: Allowing components to declare they are "waiting" for data.
  • useTransition: A hook to explicitly mark a state update as non-urgent, allowing high-priority updates (like input events) to interrupt it.

8. What are the 'use client' and 'use server' directives?

These are mandatory directives that explicitly define the execution environment within the React Server Component architecture.

'use client' Environment -> Client (Browser): To create an interactive component.Can use state, effects, and all browser APIs (e.g., onClick, localStorage).

'use server' Environment -> Server (Node.js): To create a secure, mutable API endpoint (Action).Can access backend resources (database, file system) but cannot use browser APIs.

  • Placement: use client is placed at the very top of a file.use server is typically placed at the top of an async function within an RSC, turning that function into a Server Action callable from the client.

9. How does the new use API work?
The use API is a special, non-conditional hook (though it looks like one, it can be called inside conditionals and loops, unlike standard hooks) that reads the value of a resource like a Promise or Context.

Key Difference and Value:

It simplifies Suspense integration. When use is called with a Promise, it forces the nearest Suspense boundary to show its fallback until the Promise resolves.

// Reading a Promise (simplifies Suspense integration)
// The component will suspend until the notePromise resolves.
function Note({ notePromise }) {
  const note = use(notePromise); 
  return <div>{note.title}</div>;
}

// Reading Context
function Sidebar() {
  // Unlike traditional hooks, `use` can be called conditionally
  if (!shouldShowSidebar) return null; 
  const theme = use(ThemeContext); 
  return <div className={theme}>...</div>;
}
Enter fullscreen mode Exit fullscreen mode

10. What is Document Metadata in React 19?
React 19 now offers native support for document metadata, allowing developers to render elements like <title>, <meta>, and <link> anywhere in the component tree.

How it works:

React's renderer automatically detects and hoists these tags, ensuring they are correctly placed into the document's <head> section during both initial SSR and client-side rendering. This drastically simplifies managing SEO-critical metadata, especially in complex, nested routing/layout structures.

function BlogPost({ post }) {
  return (
    <article>
      {/* Rendering metadata anywhere in the component tree */}
      <title>{post.title} | My Blog</title> 
      <meta name="author" content={post.author} />
      <h1>{post.title}</h1>
      {/* ... rest of the content */}
    </article>
  );
}
Enter fullscreen mode Exit fullscreen mode

11. What improvements were made to refs in React 19?

The primary improvement is a significant simplification of ref handling: you can now pass ref as a regular prop to function components without needing forwardRef.


12. What is the Activity component in React 19?

The <Activity> component in React 19 solves a critical problem: how to hide parts of your UI without losing state. It's like putting components to sleep while keeping their memory intact. more...


13. What is useEffectEvent Hook?
At its core, the useEffectEvent hook allows you to create stable event handlers within effects. These handlers always have access to the latest state and props, even without being included in the effect’s dependency array. However, on a broader scope, the Hook provides a solution to a subtle but common challenge that affects how effects in React handle the stale closure problem.more...


14. How does Automatic Batching work in React 19?

Automatic Batching is a performance optimization where React groups multiple state updates (setX, setY) that happen within a single execution cycle into a single, unified re-render.
Automatic Batching is applied to all state updates, regardless of their origin (event handlers, promises, timeouts, native code, etc.). This ensures that any sequence of updates always results in only one re-render, by default.

// In React 18 and 19, this sequence triggers only ONE re-render.
function handleClick() {
  setCount(c => c + 1);
  setName('New Name');
  // Even if setName was in a setTimeout, it would be batched.
}
Enter fullscreen mode Exit fullscreen mode

15. What is Partial Pre-rendering in React?

Partial Pre-rendering is a key performance strategy enabled by RSCs and Suspense. It allows the server to initially send a page as static HTML while leaving "holes" for highly dynamic or personalized content.

How it works:

Static Shell: The page's static structure and layout are served instantly (like a pre-rendered static site, excellent for Core Web Vitals).

Suspense "Holes": Dynamic sections are wrapped in <Suspense>boundaries. The server sends a lightweight placeholder for these.

Streaming: As the server fetches the data for the dynamic parts, it streams the completed HTML for those sections separately, replacing the placeholders without blocking the rest of the page.
This delivers the SEO and performance benefits of Static Site Generation (SSG) combined with the freshness and dynamism of Server-Side Rendering (SSR).


16. How has SSR improved in React 19?
The Server Component architecture fundamentally redefined and improved Server-Side Rendering (SSR) by moving data fetching to the server side.
Key Improvements:
Elimination of Client-Side Data Waterfall: Components fetch data directly on the server, eliminating the need to wait for the client to download JS, hydrate, and then start fetching data.

Intelligent Streaming & Hydration: React can stream the UI in smaller, prioritized chunks. The client starts hydrating and becoming interactive on the critical, ready-to-go parts while the slower parts are still streaming in the background.

Unified Architecture: The line between SSG and SSR is blurred. The framework (like Next.js) can cache RSC outputs intelligently, only re-rendering and re-streaming the parts that need to be fresh.


17. What are Performance Tracks in React?
"Performance Tracks" is a general, non-official term that refers to the practice of monitoring and analyzing the various stages of an application's lifecycle, especially focusing on rendering performance and bundle size.

Key Tools for Tracking Performance:
React DevTools Profiler: The primary tool for tracing component renders, measuring render time, and identifying unnecessary re-renders (which the React Compiler aims to eliminate).

Browser DevTools (Performance Tab): Essential for analyzing CPU usage, repaint/layout times, and identifying long tasks that block the main thread.

Code Splitting/Bundle Analysis: Using tools like Webpack Bundle Analyzer to break down and optimize the size of the JavaScript sent to the client.


18. How do keys work in React lists and why use unique IDs?
The key prop is a special string attribute required when rendering a list of elements. It helps React identify which items in a list have changed, been added, or been removed, which is crucial for efficient DOM manipulation.

Why Unique and Stable IDs are Non-Negotiable:
React uses the key to create a stable identity for each component in the list.

Using Index as Key (Anti-Pattern): If a list's order changes (e.g., an item is prepended or sorted), every item's index/key changes. React sees this as every component being deleted and re-created, leading to:

  1. Significant performance degradation due to unnecessary DOM manipulation.

  2. State bugs where component state (like the value of an input field) is incorrectly retained or mixed up across the wrong list items.

Using Unique ID as Key (Best Practice): By using a stable, unique identifier from your data (e.g., item.id), the identity of each item is preserved regardless of where it moves in the list, ensuring correct, efficient updates.


19. What's the difference between controlled and uncontrolled components?

This distinction relates to how form input data is managed.

Controlled Component: The form data is entirely handled by React's component state (useState).

  • Mechanism: The input's value is set by the value prop, and changes are handled by an onChange handler that updates the state.

  • Pros: Single source of truth (the React state), easy to perform immediate validation and conditional formatting, and clear data flow.

- Example:
<input value={state} onChange={handleChange} />

Uncontrolled Component: The form data is managed by the DOM itself (standard HTML behavior).

  • Mechanism: You use a ref to access the value imperatively when you need it (e.g., in a submit handler). You typically use the defaultValue prop for initial state.
  • Pros: Requires less boilerplate code, simpler for very basic forms or integrating non-React libraries.

- Example:
<input ref={inputRef} defaultValue="Initial Value" />


20. What are best practices for state management?
The state management landscape is heavily influenced by the Server Component architecture, shifting the responsibility for data fetching and caching away from client-side global stores.

1. Prioritize Server Components for Data: For any server-derived data (products, users, posts), the best practice is to fetch it directly in an RSC. This eliminates the need for complex global client-side caching solutions.

2. Colocate Local UI State: Use useState and useReducer for all local, ephemeral state (e.g., modal open/close, dropdown status). Keep state as close to its usage as possible.

3. Choose Lightweight for Global Client State: For complex, cross-cutting UI state (e.g., dark mode toggle, multi-step form state), favor minimal, low-boilerplate libraries like Zustand or Jotai over heavy solutions like Redux.

4. Use Specialized Libraries for Advanced Server Caching: If you have highly dynamic, frequently updated data or need sophisticated features like offline support and focus-refetching, TanStack Query (React Query) remains the gold standard for managing server cache on the client.

5. Leverage the URL for Global State: Use the URL and search parameters (via hooks like useSearchParams in Next.js/Remix) to manage state that should be persisted, shareable, and refresh-safe (e.g., filters, sorting, active tab).

Summary: The modern stack is about decentralization. Server Components handle data, and React's built-in hooks handle local UI logic. Global stores are reserved only for truly complex, cross-application client-side state.


21. What is hydration in React?
Hydration is the process by which React attaches event listeners and updates the DOM to match the virtual DOM on the client side. Hydration is necessary for server-side rendered React applications to ensure that the client-side rendered content is interactive and matches the server-rendered content. During hydration, React reconciles the server-rendered HTML with the client-side virtual DOM and updates the DOM to match the virtual DOM structure.

Read more about it here


22. Explain higher-order components (HOCs)?

Higher-order components (HOCs) are functions that take a component as an argument and return a new component with enhanced functionality. HOCs are used to share code between components, add additional props or behavior to components, and abstract common logic into reusable functions.

const withLogger = (WrappedComponent) => {
  return (props) => {
    console.log('Component rendered:', WrappedComponent.name);
    return <WrappedComponent {...props} />;
  };
};

const EnhancedComponent = withLogger(MyComponent);

Enter fullscreen mode Exit fullscreen mode

23. What is the purpose of the useImperativeHandle hook in React?

The useImperativeHandle hook in React is used to customize the instance value that is exposed to parent components when using React.forwardRef. It allows you to define which properties or methods of a child component's instance should be accessible to parent components when using a ref.

const ChildComponent = React.forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));

  return <input ref={inputRef} />;
});

const ParentComponent = () => {
  const childRef = useRef(null);

  const handleClick = () => {
    childRef.current.focus();
  };

  return (
    <>
      <ChildComponent ref={childRef} />
      <button onClick={handleClick}>Focus Input</button>
    </>
  );
};

Enter fullscreen mode Exit fullscreen mode

In this example, the useImperativeHandle hook is used to expose the focus method of the inputRef to the parent component when using a ref.


24. What are the benefits and limitations of using React's Context API for state management?

Benefits:

  • Simplifies State Sharing: Allows easy sharing of state across components without prop drilling.

  • Eliminates Prop Drilling: Avoids passing props through multiple layers, making the code more maintainable.
    Limitations:

  • Inefficient for Frequent Updates: Re-renders all components that consume the context on state changes, which can be inefficient for frequent updates.

  • Not Suitable for Large-Scale State Management: For complex or large applications with deep state needs, consider using state management libraries like Redux or Zustand for better performance and scalability.


25. What are React portals, and in what scenarios would you use them?
React portals provide a way to render children outside the parent component's DOM hierarchy. They are useful for modals, tooltips, or any UI element that needs to break out of the regular DOM flow but remain part of the React tree.

ReactDOM.createPortal(<Modal />, document.getElementById('modal-root'));

Enter fullscreen mode Exit fullscreen mode

26. How does React handle concurrent rendering with multiple updates and prioritize them?

React uses the priority system in Concurrent Mode to schedule updates. It can break up large updates into smaller chunks and give priority to user interactions (like clicks or input) to ensure the app remains responsive.

more ...


27. You are rendering a huge Data Grid with 10,000 rows and 50 columns. Initial render is acceptable, but any scrolling or filtering results in severe Jank and frame rate drops. How do you solve this performance problem without reducing the amount of data the user sees?

The primary solution is List Virtualization or Windowing.
Virtualization shrinks the DOM to a constant, manageable size and recycles DOM nodes instead of recreating the entire list.
In a real-world scenario, I would use established libraries like react-window or react-virtualized to handle the complex mathematical calculations and DOM recycling efficiently.


28. Scenario: Managing Nested Side Effects (Infinite Loops)
Question: You have a complex component where its useEffect calls an internal function. This internal function, in turn, updates a piece of state in the parent component. What potential problem might occur here, and how do you resolve it with minimal hassle?

This scenario is a classic pattern for creating Infinite Re-render Loops or unpredictable side effects.

The Problem: If the inner function (called inside useEffect) is defined without useCallback, it gets a new Identity on every parent render. If this function is listed in the useEffect dependency array, it forces the useEffect to re-run on every render, which updates the parent's state, causing a new render, and the cycle continues.

  • Use useCallback: The inner function should be memoized using useCallback. This ensures the function's identity only changes when its own dependencies change, stabilizing the useEffect.

  • Updater Function in useState: If the inner function only updates the parent state based on its previous value (e.g., setCount(c => c + 1)), you don't need to list the setCount function in the dependency array, as its identity is guaranteed by React.

  • Best Practice (React 19+): Use useEffectEvent (as previously discussed) to completely decouple the function from the useEffect dependencies while still safely accessing the latest props and state.


29. What is BroadcastChannel and when do you use it?
BroadcastChannel is a modern Web API that allows real-time communication between multiple browser tabs, windows, or iframes of the same origin. It’s extremely useful when you need cross-tab state synchronization without relying on heavy solutions like WebSockets or server-side events. I typically use BroadcastChannel to synchronize authentication state (e.g., instant logout across all tabs), theme changes, language changes, or any global UI settings. It provides a simple publish/subscribe model, low latency messaging, and avoids the side effects of the localStorage ‘storage’ event. Overall, it’s the cleanest and most reliable way to keep multiple browser contexts in sync.


30. How to Search for an ID or Attribute Inside a Large String ?

Depending on the data format, I use different strategies.
For simple substrings, I prefer includes() or indexOf() because they are extremely fast.
For patterns, I use regex.
If the data is HTML, I parse it with DOMParser and query it.
If it’s JSON, I convert it to an object and search through it.
For huge datasets, I use streaming or build an index for O(1) lookup.
And in performance-critical cases, I still use classic for loops.

Top comments (0)