Skip to the content.
18 July 2026
[React](https://github.com/facebook/react) uses a lightweight, in-memory representation of the actual DOM called the [virtual DOM](https://reactjs.org/docs/faq-internals.html) to optimize UI updates. When a component's state changes, React computes the diff between the virtual DOM and the real DOM, applying only the necessary changes. While this process is fast, complex applications can still run into performance bottlenecks due to unnecessary component re-renders, heavy computations, or bloated bundle sizes. This guide outlines practical strategies and APIs you can use to optimize your React components. ## Practical ways to optimize React components ### 1. Wrap functional components with React.memo `React.memo` is a higher-order component that prevents functional components from re-rendering if their props have not changed. It is the functional component equivalent of `React.PureComponent`. ```jsx import { memo } from "react"; const MyComponent = ({ name }) => { console.log("Rendering MyComponent"); return

Hello, {name}!

; }; export default memo(MyComponent); ``` By default, `React.memo` performing a shallow comparison of props is sufficient. If you need fine-grained control, you can pass a custom comparison function as the second argument: ```jsx import { memo } from "react"; const MyComponent = ({ name, age }) => { console.log("Rendering MyComponent"); return (

Hello, {name}!

You are {age} years old.

); }; export default memo(MyComponent, (prevProps, nextProps) => { // Only re-render if name or age actually changed return prevProps.name === nextProps.name && prevProps.age === nextProps.age; }); ``` ### 2. Lazy-load components with React.lazy and Suspense You can reduce your application's initial bundle size and speed up load times by deferring the loading of components that are not immediately visible on the screen. ```jsx import React, { lazy, Suspense } from "react"; const MyComponent = lazy(() => import("./MyComponent")); function App() { return ( Loading...}> ); } ``` The `Suspense` container handles the loading state, rendering your fallback UI while the chunk containing `MyComponent` is fetched over the network. ### 3. Cache expensive calculations with useMemo The `useMemo` hook lets you cache the result of a calculation between renders, recomputing it only when one of its dependencies changes. ```jsx import { useMemo } from "react"; function MyComponent({ data }) { const filteredData = useMemo(() => { return data.filter((item) => item.name.startsWith("A")); }, [data]); return ( ); } ``` In this example, filtering the array only runs when the `data` prop changes, preventing expensive array operations on every render. ### 4. Cache functions with useCallback When you pass a callback function to a memoized child component, React recreates that function on every render. This triggers a re-render in the child component despite prop memoization. Use `useCallback` to cache the function instance itself. ```jsx import { useCallback } from "react"; function MyComponent({ data }) { const handleClick = useCallback(() => { console.log(data); }, [data]); return ; } ``` ### 5. Control effects with dependency arrays Uncontrolled `useEffect` hooks that run on every render can cause performance lag or infinite rendering loops. Always declare an explicit dependency array to control when your side effects run. ```jsx import { useEffect, useState } from "react"; function MyComponent({ data }) { const [count, setCount] = useState(0); useEffect(() => { console.log(`The data is ${data}`); }, [data]); // Runs only when data changes return (

You clicked {count} times

); } ``` ### 6. Implement shouldComponentUpdate in class components If you are maintaining older class components, you can use the `shouldComponentUpdate` lifecycle method to manually determine whether prop or state changes warrant an update. ```jsx class MyComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return nextProps.name !== this.props.name; } render() { return

Hello, {this.props.name}!

; } } ``` ### 7. Use PureComponent for class components Alternatively, extend `React.PureComponent` instead of `React.Component`. Pure components implement a default `shouldComponentUpdate` with a shallow comparison of props and state. ```jsx class MyComponent extends React.PureComponent { render() { return

Hello, {this.props.name}!

; } } ``` ### 8. Use React Fragments to avoid extra DOM nodes Using unnecessary wrapper elements like `div` increases the size of your browser's DOM tree, which can slow down page rendering. Use React Fragments to group elements instead. ```jsx function List({ items }) { return ( {items.map((item) => ( {item.text} ))} ); } ``` This groups your elements without inserting wrapper nodes into the final HTML output. ### 9. Be explicit instead of spreading props Spreading props using the JSX spread operator (`...`) can make your code harder to read and optimize. It also runs the risk of passing invalid HTML attributes directly to DOM elements. Instead of passing everything in an object: ```jsx // Avoid this ``` Explicitly define the props your component needs: ```jsx // Prefer this ``` This makes data dependencies clear and prevents unexpected prop updates from triggering unnecessary renders. ### 10. Virtualize long lists Rendering thousands of list items at once can make your application sluggish and unresponsive. List virtualization speeds up rendering by drawing only the elements currently visible in the viewport. Libraries like [react-virtualized](https://github.com/bvaughn/react-virtualized) or [react-tiny-virtual-list](https://github.com/clauderic/react-tiny-virtual-list) provide robust, pre-built components to handle list virtualization and smooth scrolling. ### 11. Divide code chunks with code-splitting For large-scale applications, [code-splitting](https://reactjs.org/docs/code-splitting.html) allows you to split your final JavaScript bundle into smaller, page-specific chunks. These chunks are loaded dynamically on demand as the user navigates your site. ### 12. Profile performance with React Developer Tools Use the profiling tab in the [React Developer Tools](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) browser extension to capture performance traces. The profiler shows exactly which components re-rendered, how long they took to render, and why the update occurred. ### Helpful resources - [React documentation on performance optimization](https://reactjs.org/docs/optimizing-performance.html) - [React Profiler introduction](https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)