17 React Interview Questions You Must Know as a Developer in 2025

Originally published on Medium React is one of the most in-demand skills for Frontend Developers in 2025. If you're preparing for a React developer interview in 2025, it's crucial to stay ahead of the curve with the latest best practices, patterns, and concepts. This article compiles a list of 17 React interview questions that cover everything from core React principles to advanced performance optimizations that will help you ace your next React developer interview. Let's dive in! 1. What is the React Virtual DOM? How is it different from the real DOM & Shadow DOM? Virtual DOM is a concept where a virtual representation of the real DOM is kept inside the memory and is synced with the actual DOM by a library such as ReactDOM only when necessary. The Virtual DOM is an object that represents the real DOM in the memory. Since DOM updates are an integral part of any web app but are the costliest operation in frontend development, the Virtual DOM is utilized to check for parts of the app that need to be updated & update only those parts, thus significantly boosting performance. The Virtual DOM is a completely different concept from the real DOM and shadow DOM. The real DOM is the actual representation of the HTML document in a tree-like structure that browsers use to track the contents of a web page, and the Shadow DOM is a programming practice that allows developers to create isolated reusable components for web applications. If you want to dive deeper into the differences between Virtual DOM, real DOM & the Shadow DOM, check out this article 2. What are the 2 types of components in React? Where should we use them? There are 2 types of components in React: Class Components Functional Components Previously, class components were the only way to create components with any functionality in React, with the functional components being used only as presentational components and often being referred to as "dumb" components. But, with the release of React 16.8 and the introduction of React Hooks, functional components can now have state & lifecycle methods, making them the preferred way to create components in React. Functional components are much faster than class components with less overhead & boilerplate code, so it's recommended to use functional components wherever possible. However some of the lifecycle methods are still available only in class components, so you might need to use class components in some niche cases like creating your custom error boundaries (eg: using the componentDidCatch lifecycle method in class components). 3. Why do we need keys in React? Can we use keys without lists in React? Keys in React are used to identify unique Virtual DOM Elements with their corresponding data driving the UI. Using keys helps React optimize rendering by recycling existing DOM elements. Keys help React identify which items have changed, are added, or are removed, enabling it to reuse already existing DOM elements, thus providing a performance boost. For example: const Todos = ({ todos }) => { return ( {todos.map((todo) => ( {todo.text} ))} ); }; This would cause new DOM Elements to be created every time todos change, but adding the key prop (ie: {todo.text}) would result in "dragging" around the DOM Elements inside the ul tag & updating only the necessary lis Keys can be used with any element in React & does not necessarily need to be used with lists, but it's most commonly used with lists to optimize rendering. Some non-standard (& non-recommended) use cases of keys include using them to force the re-rendering of components like the example below: const TimeNow = () => { const [key, setKey] = useState(0); useEffect(() => { const interval = setInterval(() => setKey((prevKey) => prevKey + 1), 1000); return () => clearInterval(interval); }, []); return {new Date()}; }; As mentioned, code like this should definitely be avoided & you are better off using the state to store the time instead of the key to force re-rendering. Using keys to force re-rendering is an anti-pattern & can lead to severe performance issues unless you know exactly what you are doing. 4. Difference between controlled and uncontrolled input in React Controlled components rely on the React state to manage the form data, while uncontrolled components use the DOM itself to handle form data. In most cases, controlled components are preferred because they provide a single source of truth for the form data, making it easier to manage, validate, and submit the form data. const ControlledInputForm = () => { const [value, setValue] = useState(""); const handleChange = (e) => setValue(e.target.value); const handleSubmit = (e) => { e.preventDefault(); console.log(value); }; return ( Submit ); }; const UncontrolledInputForm = () => { const inputRef = useRef(null); const ha

Mar 30, 2025 - 07:07
 0
17 React Interview Questions You Must Know as a Developer in 2025

Originally published on Medium

React is one of the most in-demand skills for Frontend Developers in 2025. If you're preparing for a React developer interview in 2025, it's crucial to stay ahead of the curve with the latest best practices, patterns, and concepts.

This article compiles a list of 17 React interview questions that cover everything from core React principles to advanced performance optimizations that will help you ace your next React developer interview.

Let's dive in!

dive-in

1. What is the React Virtual DOM? How is it different from the real DOM & Shadow DOM?

Virtual DOM is a concept where a virtual representation of the real DOM is kept inside the memory and is synced with the actual DOM by a library such as ReactDOM only when necessary.

The Virtual DOM is an object that represents the real DOM in the memory. Since DOM updates are an integral part of any web app but are the costliest operation in frontend development, the Virtual DOM is utilized to check for parts of the app that need to be updated & update only those parts, thus significantly boosting performance.

The Virtual DOM is a completely different concept from the real DOM and shadow DOM. The real DOM is the actual representation of the HTML document in a tree-like structure that browsers use to track the contents of a web page, and the Shadow DOM is a programming practice that allows developers to create isolated reusable components for web applications.

If you want to dive deeper into the differences between Virtual DOM, real DOM & the Shadow DOM, check out this article

2. What are the 2 types of components in React? Where should we use them?

There are 2 types of components in React:

  1. Class Components
  2. Functional Components

Previously, class components were the only way to create components with any functionality in React, with the functional components being used only as presentational components and often being referred to as "dumb" components.

But, with the release of React 16.8 and the introduction of React Hooks, functional components can now have state & lifecycle methods, making them the preferred way to create components in React.

Functional components are much faster than class components with less overhead & boilerplate code, so it's recommended to use functional components wherever possible. However some of the lifecycle methods are still available only in class components, so you might need to use class components in some niche cases like creating your custom error boundaries (eg: using the componentDidCatch lifecycle method in class components).

3. Why do we need keys in React? Can we use keys without lists in React?

keys

Keys in React are used to identify unique Virtual DOM Elements with their corresponding data driving the UI. Using keys helps React optimize rendering by recycling existing DOM elements.

Keys help React identify which items have changed, are added, or are removed, enabling it to reuse already existing DOM elements, thus providing a performance boost.

For example:

const Todos = ({ todos }) => {
  return (
    <div>
      {todos.map((todo) => (
        <li>{todo.text}li>
      ))}
    div>
  );
};

This would cause new DOM Elements to be created every time todos change, but adding the key prop (ie:

  • {todo.text}
  • ) would result in "dragging" around the DOM Elements inside the ul tag & updating only the necessary lis

    Keys can be used with any element in React & does not necessarily need to be used with lists, but it's most commonly used with lists to optimize rendering. Some non-standard (& non-recommended) use cases of keys include using them to force the re-rendering of components like the example below:

    const TimeNow = () => {
      const [key, setKey] = useState(0);
    
      useEffect(() => {
        const interval = setInterval(() => setKey((prevKey) => prevKey + 1), 1000);
        return () => clearInterval(interval);
      }, []);
    
      return <div key={key}>{new Date()}div>;
    };
    

    As mentioned, code like this should definitely be avoided & you are better off using the state to store the time instead of the key to force re-rendering. Using keys to force re-rendering is an anti-pattern & can lead to severe performance issues unless you know exactly what you are doing.

    4. Difference between controlled and uncontrolled input in React

    Controlled components rely on the React state to manage the form data, while uncontrolled components use the DOM itself to handle form data.

    In most cases, controlled components are preferred because they provide a single source of truth for the form data, making it easier to manage, validate, and submit the form data.

    const ControlledInputForm = () => {
      const [value, setValue] = useState("");
      const handleChange = (e) => setValue(e.target.value);
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log(value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input type="text" value={value} onChange={handleChange} />
          <button type="submit">Submitbutton>
        form>
      );
    };
    
    const UncontrolledInputForm = () => {
      const inputRef = useRef(null);
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log(inputRef.current.value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input type="text" ref={inputRef} />
          <button type="submit">Submitbutton>
        form>
      );
    };
    

    5. Why do we need to transpile JSX code?

    Unless you hurt your head, you are NOT using React like this:

    import { createElement } from "react";
    
    const Greeting = ({ name }) => {
      return createElement("h1", { className: "greeting" }, "Hello");
    };
    

    Yet, this is what the browser reads - it is simply unable to understand the JSX syntax commonly used to write React components.

    Thus we are required to use tools like Babel to transpile JSX to JavaScript so that the browser can execute it. So when you write the following code:

    const Greeting = ({ name }) => <h1 className="greeting">Helloh1>;
    

    It's transpiled to the former code snippet so that the browser can interpret it & render the component.

    6. How does JSX prevent injection attacks?

    injection

    Since JSX renders content as text, any element in user input is not treated as HTML, just as plain text. For example, the following script tag would be rendered as text and not executed:

    const MyComponent = () => {
      const content = "";
      return <div>{content}div>;
    };
    

    NOTE: You can override this behavior by using dangerouslySetInnerHTML but it's not recommended unless you are absolutely sure about the source of the input (& would strongly suggest sanitizing the content before injecting it).

    const MyComponent = () => {
      const content = "";
      return <div dangerouslySetInnerHTML={{ __html: content }} />;
    };
    

    7. How can we add styling to React components?

    CSS Files

    Using CSS files is one of the most common ways to style React components. It allows for the use of all CSS features and is set up by default with the Create React App.

    /* Button.css */
    .button {
      background-color: blue;
      color: white;
    }
    
    // Button.tsx
    import "./Button.css";
    
    const Button = () => {
      return <button className="button">Click mebutton>;
    };
    

    Inline CSS

    Styling React elements using inline CSS allows styles to be completely scoped to an element. However, certain styling features are not available with inline styles. For example, the styling of pseudo-classes like :hover.

    const Button = () => {
      return (
        <button style={{ backgroundColor: "blue", color: "white" }}>
          Click me
        button>
      );
    };
    

    CSS-in-JS Modules (Styled Components, Emotion, and Styled-jsx)

    CSS-in-JS modules are a popular option for styling React applications because they integrate closely with React components. For example, they allow styles to change based on React props at runtime. Also, by default, most of these systems scope all styles to the respective component being styled.

    import styled from "styled-components";
    
    const Button = styled.button`
      background-color: blue;
      color: white;
    `;
    
    const App = () => {
      return <Button>Click meButton>;
    };
    

    CSS Modules

    My personal favorite styling method, CSS Modules allow styles to be scoped to a single component. It's a great way to avoid class name collisions (fancy term for 2 classes ending up with the same name - quite common in a large scale project), keep styles organized & add shared styles to multiple components.

    /* Button.module.css */
    .button {
      background-color: blue;
      color: white;
    }
    
    // Button.js
    import styles from "./Button.module.css";
    
    const Button = () => {
      return <button className={styles.button}>Click mebutton>;
    };
    

    8. What are synthetic events in React?

    Synthetic events combine the response of different browser's native events into one API, ensuring that the events are consistent across different browsers. The application is consistent regardless of the browser it is running in.

    const Component = () => {
      const handleClick = (e) => {
        e.preventDefault(); // synthetic event
        console.log("link clicked");
      };
      return <a onClick={(e) => handleClick}>Click mea>;
    };
    

    9. What is the strict mode in React?

    is a component included with React to provide additional visibility of potential issues in components. If the application is running in development mode, any arising issue is logged to the development console, but these warnings are not shown if the application is running in production mode.

    Developers use to find problems such as deprecated lifecycle methods and legacy patterns, to ensure that all React components follow current best practices.

    can be applied at any level of an application component hierarchy, which allows it to be adopted incrementally within a codebase.

    The docs for are added below

    Strict Mode enables the following development-only behaviors:

    10. How to gracefully handle errors in React?

    By default, if a React app throws an error during rendering, React will remove its UI from the screen. To prevent this, we can wrap a part of the UI into an error boundary, which would allow us to catch the error & display a fallback UI instead of crashing the entire app.

    You can build your custom error boundary:

    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        // Update state so the next render will show the fallback UI.
        return { hasError: true };
      }
    
      render() {
        if (this.state.hasError) {
          // You can render any custom fallback UI, or even accept it as a prop
          return <FallbackComponent />;
        }
        return this.props.children;
      }
    }
    

    But for most cases, you can use the react-error-boundary package which provides the necessary components to handle errors in React applications.

    import { ErrorBoundary } from "react-error-boundary";
    
    const App = () => (
      <ErrorBoundary FallbackComponent={FallbackComponent}>
        <MyComponent />
      ErrorBoundary>
    );
    

    11. What are the rules of React Hooks?

    There are 3 main rules of React Hooks:

    1. Only call hooks from React Functions: Hooks can only be called within a React Function Component or from within another hook. Calling hooks within regular JS/TS functions will be treated as a regular function call & will not work as expected.
    2. Only call hooks at the Top Level: Hooks can only be called at the top level of a React Function Component or a custom hook. This is to ensure that hooks are called in the same order each time a component renders. Using hooks inside loops, conditions, or nested functions will result in errors.
    3. Hooks must start with 'use': All hook names (including custom hooks) must begin with the word "use". This is to ensure that React can identify hooks and enforce the rules of hooks. For example, useState, useEffect, useContext, etc.

    12. How to handle the common lifecycle methods in React Functional Components?

    The common lifecycle methods in React are:

    • componentDidMount: called after the component is mounted on the DOM. Commonly used to fetch data or perform side effects like adding event listeners.
    • componentDidUpdate: called after some specific value in the component is updated. Commonly used to perform side effects based on the updated value.
    • componentWillUnmount: a cleanup method called before the component is unmounted from the DOM. Commonly used to remove event listeners or cancel network requests.

    In functional components, these lifecycle methods can be handled using the useEffect hook. The useEffect hook takes a function as its first argument and an array of dependencies as its second argument.

    const Component = () => {
      useEffect(() => {
        // componentDidMount
        console.log("Component mounted");
    
        return () => {
          // componentWillUnmount
          console.log("Component unmounted");
        };
      }, []); // empty dependency array implies this effect runs only once when the component mounts
    
      useEffect(
        () => {
          // componentDidUpdate
          console.log("Component updated");
        },
        [
          /* dependencies - changes to these values should trigger the function to re-run */
          /* NOTE: This function will run during mount too */
        ]
      );
    
      return <React.Fragment />;
    };
    

    13. What are refs in React?

    Refs are variables that allow you to persist data between renders, just like state variables, but unlike state variables, updating refs does NOT cause the component to re-render.

    Refs are usually used to (but not restricted to) store references to DOM elements.

    const Component = () => {
      const inputRef = useRef(null);
      const handleClick = () => inputRef.current.focus();
    
      return (
        <div>
          <input ref={inputRef} type="text" />
          <button onClick={handleClick}>Focus Inputbutton>
        div>
      );
    };
    

    14. What is prop drilling in React? How can we avoid it?

    drill

    While developing React applications, there is often a need to pass data from a component that is higher in the hierarchy to a component that is deeply nested. Prop drilling refers to the process of passing props from a source component to the deeply nested component through all the intermediate components.

    The disadvantage of using prop drilling is that the components that should otherwise be not aware of the data have access to the data, moreover, the code becomes harder to maintain.

    Prop drilling can be avoided using the Context API or some form of State Management library.

    15. Describe some of the optimization techniques in React

    Using memoization

    useMemo is a React hook that is used for caching CPU-Expensive functions. A CPU-Expensive function called repeatedly due to re-renders of a component can lead to severe performance issues & UX degradation.

    The useMemo hook can be used to cache the results from such functions. By using useMemo, the CPU-Expensive function gets called only when it is needed.

    Lazy Loading

    Lazy loading is a technique used to reduce the initial load time of a React app. It helps reduce the risk of web app performances to a minimum, by loading up the components as the user navigates through the app.

    Throttling and Debouncing

    Although this is not a React-specific optimization technique, it is often used in React applications to improve performance. Throttling and debouncing are techniques used to limit the number of times a function is called in response to an event - they are often used with inputs that provide real-time feedback to the user (eg: typing in a search field with auto-suggestions - the API call to fetch the suggestions can be throttled or debounced to avoid making unnecessary API calls)

    16. What are portals?

    portal

    Portal is a recommended way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. It is advisable to create a new DOM node for the portal.

    const Portal = ({ children }) => {
      // NOTE: it is assumed that the portal root is already present in the HTML file
      //       
    const portalRoot = document.getElementById("portal-root"); return ReactDOM.createPortal(children, portalRoot); };

    17. What is React Fiber?

    React Fiber is a concept of ReactJS that is used to render a system faster and smoother. It is an internal engine change aimed at making React faster & somewhat "smarter". The Fiber reconciler, which became the default reconciler for React 16 and above, is a complete rewrite of React’s reconciliation algorithm to solve some long-standing issues in React.

    Because Fiber is asynchronous, React can:

    • Pause, resume, and restart rendering work on components as new updates come in
    • Reuse previously completed work and even abort it if not needed
    • Split work into chunks and prioritize tasks based on importance

    This change allows React to break away from the limits of the previous synchronous Stack Reconciler, where tasks couldn’t be interrupted. This change also allows React to fine-tune rendering components, ensuring that the most important updates happen as soon as possible.

    Are you scratching your head, wondering what "reconciliation" is?

    head-scratch

    Don't worry, we got you covered! In React, reconciliation is the core mechanism responsible for efficiently updating the UI in response to changes in a component's state or props. It determines the minimal set of operations needed to transform the actual DOM to match the desired state represented by the virtual DOM.

    That's all folks!