reactjs
/

React Performance – Optimization Techniques & Bottlenecks

Last Sync: Today

On this page

6
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

reactjs

React Performance – Optimization Techniques & Bottlenecks

Understanding the Render Cycle

React is fast, but unnecessary re-renders can slow down your application. A re-render happens when a component's state or props change. Performance optimization in React is primarily about ensuring that components only re-render when absolutely necessary.

  1. Memoization with React.memo

By default, when a parent component re-renders, all its children re-render too. Wrapping a child component in React.memo tells React to skip rendering the component if its props haven't changed (using shallow comparison).

React JSXRead-only
1
import React from 'react';

const ExpensiveChild = React.memo(({ data }) => {
  console.log('Rendering expensive child...');
  return <div>{data}</div>;
});

  1. Stabilizing References: useCallback & useMemo

If you pass a function or an object as a prop to a memoized component, React.memo will fail because functions and objects are recreated on every render (new memory reference). Use useCallback for functions and useMemo for objects/arrays to keep references stable.

React JSXRead-only
1
// Stabilizing a function
const handleClick = useCallback(() => {
  doSomething(id);
}, [id]);

// Stabilizing a computed object
const settings = useMemo(() => ({
  theme: isDark ? 'dark' : 'light',
  fontSize: '16px'
}), [isDark]);

  1. Windowing & Virtualization

Rendering 10,000 DOM nodes at once will crash most browsers. 'Windowing' (or Virtualization) is a technique where you only render the items currently visible in the viewport. As the user scrolls, nodes are recycled.

  • React-window: A lightweight library for rendering large lists and tabular data.
  • React-virtualized: A more feature-heavy alternative for complex grid layouts.
  • Performance Gain: Reduces initial load time and memory usage by 90% in large lists.

  1. Transition API (useTransition)

Introduced in React 18, the useTransition hook allows you to mark state updates as 'non-urgent'. This keeps the UI responsive (e.g., typing in an input) while a heavy update (e.g., filtering a list) happens in the background.

React JSXRead-only
1
const [isPending, startTransition] = useTransition();

const handleChange = (e) => {
  setInputValue(e.target.value); // Urgent: Update input
  
  startTransition(() => {
    setSearchQuery(e.target.value); // Non-urgent: Update list
  });
};

Optimization Comparison

TechniqueProblem SolvedWhen to use
React.memoUnnecessary child re-rendersPure components with stable props
useCallbackChanging function referencesPassing functions to memoized children
Lazy LoadingLarge bundle size / slow initial loadRoutes or heavy components
VirtualizationDOM bloat from large listsLists/Grids with >100 items
useDeferredValueUI lag during state updatesFiltering or heavy visual computations

Try it yourself

import React, { useState, useMemo } from 'react';

export default function PerformanceDemo() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // This heavy calculation only re-runs if 'count' changes
  // Typing in the input won't trigger this logic!
  const expensiveValue = useMemo(() => {
    console.log('Running expensive logic...');
    let sum = 0;
    for (let i = 0; i < 1000000; i++) sum += i;
    return sum + count;
  }, [count]);

  return (
    <div style={{ padding: '20px' }}>
      <h3>Performance Optimization</h3>
      <p>Expensive Result: {expensiveValue}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment Count (Slow Update)</button>
      
      <div style={{ marginTop: '20px' }}>
        <input 
          placeholder="Type here (Fast Update)..." 
          value={text} 
          onChange={e => setText(e.target.value)} 
        />
        <p>You typed: {text}</p>
      </div>
    </div>
  );
}

Test Your Knowledge

Q1
of 2

Which tool helps identify which components are re-rendering unnecessarily?

A
Redux DevTools
B
Chrome Network Tab
C
React DevTools Profiler
D
Lighthouse
Q2
of 2

What is the result of wrapping a component in React.memo?

A
It makes the component invisible
B
It prevents the component from re-rendering if its props are the same
C
It automatically fetches data for the component
D
It converts the component to a class component

Frequently Asked Questions

Should I use useMemo everywhere?

No. Memoization has its own cost (memory and comparison logic). Use it only for truly expensive calculations or to maintain referential integrity for memoized children.

How do I find performance bottlenecks?

Use the 'Profiler' tab in React DevTools. It records why each component rendered and how long it took.

What is 'Code Splitting'?

Using React.lazy and Suspense to load parts of your application only when they are needed, reducing the size of the initial JavaScript bundle.

Previous

react async handling

Next

react code splitting

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.