Find the top React Performance Optimization Techniques specific to React applications that help to make your react app faster and more responsive for the users along with some bonus techniques.
Anuj Sharma
Last Updated Nov 3, 2025
Performance optimization of a react application is necessary while building any react application, there are many performance optimization techniques exist to improve performance using the JavaScript, CSS or network optimization side, but in this post we primarily focused on the performance optimization techniques which we can use specific to React codebase and patterns. Let's checkout these top 10 and bonus React performance optimization techniques
React.memo is a higher-order component that memoizes the functional component props. It prevents unnecessary re-renders by comparing the previous and current props
In the below example, Child is wrapped in React.memo() so
count changes.text changes and return the Memoized component.import React, { useState } from "react";
// Child Component
const Child = React.memo(({ count }) => {
  console.log("Child rendered!");
  return <h2>Child Count: {count}</h2>;
});
// Parent Component
function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  return (
    <div>
      <Child count={count} />
      <button onClick={() => setCount((c) => c + 1)}>Increment Count</button>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something"
      />
    </div>
  );
}
export default Parent;
Use PureComponent or shouldComponentUpdate to specify when the component should be re-rendered by shallow comparison of props.
class MyComponent extends React.PureComponent {
   // Component logic here
} 
When working with large lists, consider using virtualized list (library like react-virtualized or react-window) to render only the visible items in the viewport, that improves performance significantly by just allowing the visible part in the DOM
Split your application into smaller chunks using dynamic imports or React.lazy to load components only when needed, reducing the initial bundle size and improving load times. Use Suspense to show the fallback option while modules are lazy loaded. Checkout the below example
import React, { Suspense, lazy, useState } from "react";
// Lazy load the components
const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./About"));
function App() {
  const [page, setPage] = useState("home");
  return (
    <div>
      <button onClick={() => setPage("home")}>Home</button>
      <button onClick={() => setPage("about")}>About</button>
      {/* Suspense shows fallback while component is loading */}
      <Suspense fallback={<p>Loading...</p>}>
        {page === "home" ? <Home /> : <About />}
      </Suspense>
    </div>
  );
}
export default App;
React's useMemo hook allows you to memoize expensive computations and calculations, ensuring that they are only re-executed when dependencies change.
import React, { useState, useMemo } from "react";
function ExpensiveCal() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  // Expensive computation
  const expensiveValue = useMemo(() => {
    console.log("Doing Expensive calculation...");
    let total = 0;
    for (let i = 0; i < 1000000000; i++) {
      total += i;
    }
    return total + count;
  }, [count]); // ✅ Only re-run when "count" changes
  return (
    <div>
      <h2>Expensive Value: {expensiveValue}</h2>
      <button onClick={() => setCount((c) => c + 1)}>Increment Count</button>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something..."
      />
    </div>
  );
}
React uses keys to optimize the rendering of the component, so use unique and stable keys whenever rendering any list of elements helps react to improve rendering. Avoid using index and random function to generate keys.
import React from "react";
function UserList() {
  const users = [
    { id: 1, name: "FrontendGeek" },
    { id: 2, name: "Anuj" },
    { id: 3, name: "Sharma" },
  ];
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}> // ✅Unique Key
             {user.name}
        </li>
      ))}
    </ul>
  );
}
Instead of multiple context consumers, use the useContext hook to subscribe to a context provider, reducing the number of re-renders and improving performance.
import React, { createContext, useContext } from "react";
const ThemeContext = createContext();
const UserContext = createContext();
function Profile() {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);
  return (
    <div style={{ background: theme.background, color: theme.text }}>
      <h2>User: {user.name}</h2>
    </div>
  );
}
export default function App() {
  const theme = { background: "black", text: "white" };
  const user = { name: "FrontendGeek" };
  return (
    <ThemeContext.Provider value={theme}>
      <UserContext.Provider value={user}>
        <Profile />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}
Inline function as part of the react functional component redeclared every time when component renders, wrapping up the function with useCallback hook preserves the function reference and thats why there is no need to re-declare function on every render.
Even avoid using the inline function calls on the events directly as part of the component itself
import React, { useState, useCallback } from "react";
const Child = React.memo(({ onClick }) => {
  console.log("Child rendered");
  return <button onClick={onClick}>Click Me</button>;
});
function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  // ✅ useCallback memoizes the function reference
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []); // no dependencies, return same function reference always
  return (
    <div>
      <Child onClick={handleClick} />
      <p>Count: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>Increment Count</button>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something"
      />
    </div>
  );
}
Debounce or throttle user input events using libraries like lodash or custom implementations to control the frequency of function executions, improving performance in data-heavy applications.
learn more about Debouncing & Throttling
Always clean up any asynchronous task when the component is unmounted such as setTimeout or setInterval to reduce the memory consumption and avoid memory leakage which can lead to performance issues if keep going for a extended duration.
import React, { useEffect, useState } from "react";
function TimerComponent() {
  const [time, setTime] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => {
      setTime((t) => t + 1);
    }, 1000);
    // Cleanup interval on unmount
    return () => {
      clearInterval(interval);
      console.log("Timer cleaned up");
    };
  }, []);
  return <div>Time: {time}</div>;
}
Change in dynamic data where we doesn't requires update in any part of the react component than its better to store that data in ref instead of state. If you store as part of the state it will re-render the react component but since there is no change required it's totally waste.
Avoid importing directly from the root this will help you to keep only the required modules from the library which you are using in your application and it helps to keep the minimal bundle size -> Faster bundle loading -> faster react application, for example
import {get} from 'lodash ❌
import get from 'lodash/get' ✅
As props can change anytime, only use props that you need, and don’t add those that are not required.
There are many more optimization techniques can be used for specific applications but here we have covered the most common react performance optimization techniques only. Understanding Implementing these React performance optimization techniques can significantly enhance the speed and responsiveness of your React applications.
By leveraging memoization, lazy loading, virtualization, and other best practices, you can create high-performing optimized React applications.
Advertisement
Advertisement
Alok Kumar Giri
Last Updated Jun 2, 2025
Code snippet examples which will help to grasp the concept of Hoisting in JavaScript, with solutions to understand how it works behind the scene.
Anuj Sharma
Last Updated Nov 3, 2025
Find the step-by-step explanation of the useFetch custom hook in React that helps in fetching the data from an API and handling loading, error states.
Anuj Sharma
Last Updated Oct 2, 2025
Explore Polyfill for map, filter and reduce array methods in JavaScript. A detailed explanation of Map, filter and reduce polyfills in JS helps you to know the internal working of these array methods.
Anuj Sharma
Last Updated Aug 3, 2025
Explore the implementation of setTimeout in JavaScript with a detailed explanation for every step. Understand all scenarios expected to implement the setTimeout polyfill.
Anuj Sharma
Last Updated Oct 26, 2025
In this post, we will going to cover the step-by-step implementation of Infinite Currying Sum with a code example. This is one of the most common JavaScript Interview questions.
Anuj Sharma
Last Updated Oct 26, 2025
Understand the step-by-step implementation of Infinite Currying Multiplication in JavaScript with a code example.
Subscribe to FrontendGeek Hub for the frontend interview preparation, interview experiences, curated resources and roadmaps.
© 2024 FrontendGeek. All rights reserved