DevAcademy
LearnReactPerformance Optimization
AdvancedReact

Performance Optimization

Learn practical techniques for avoiding unnecessary re-renders and reducing bundle size in React apps.

Reading Time

18 min

Lesson

Lesson 35 of 42

Why a Component Re-Renders

A component re-renders when its own state changes, when its parent re-renders (by default, all children re-render too), or when a context it consumes changes — most performance problems come from one of these three triggers firing more often than necessary.

React.memo

React.memo wraps a component so it skips re-rendering when its props haven’t changed (compared shallowly), even if its parent re-renders.

Skipping Re-Renders with React.memo

const ExpensiveRow = React.memo(function ExpensiveRow({ item }) {
  console.log("Rendering row:", item.id);
  return <li>{item.name}</li>;
});

// If the parent re-renders but 'item' didn't change,
// ExpensiveRow skips re-rendering entirely.

React.memo Only Helps with Stable Props

If a prop is a new object, array, or function created fresh on every render, React.memo’s shallow comparison always sees it as "different," defeating the optimization. This is why React.memo is often paired with useMemo/useCallback on the parent’s side.

Code Splitting

Rather than shipping the entire app as one large JavaScript bundle, code splitting breaks it into smaller chunks that load on demand — reducing the amount of code the browser needs to download and parse before the first meaningful render.

Code Splitting a Route

import { lazy, Suspense } from "react";

const SettingsPage = lazy(() => import("./SettingsPage"));

function App() {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <SettingsPage />
    </Suspense>
  );
}
// SettingsPage's code is only downloaded when this component actually renders

Virtualizing Long Lists

Rendering thousands of DOM nodes at once (like a long table or feed) is expensive regardless of memoization. A virtualization library (like react-window or TanStack Virtual) renders only the items currently visible in the viewport, recycling DOM nodes as the user scrolls.

Common Optimization Techniques

TechniqueSolves
React.memoA child re-rendering when its own props haven’t changed
useMemo / useCallbackRecreating expensive values/functions on every render
Code splitting (lazy + Suspense)Shipping too much JavaScript up front
List virtualizationRendering huge numbers of DOM nodes at once
Keying/structuring state wellAvoiding unnecessary re-renders from state changes higher than needed

Measure Before You Optimize

The React DevTools Profiler shows exactly which components re-rendered, how long they took, and why. Optimizing without profiling first often targets the wrong component, or adds complexity for no measurable benefit.

Best Practice

Structure state so that changes only affect the smallest part of the tree that actually needs to update — many performance problems disappear once state lives at the right level, before any memoization is even needed.

Interview Questions

Quick Quiz

1. What does React.memo do?

2. Why can React.memo fail to prevent a re-render even when data hasn’t "really" changed?

3. What is the purpose of list virtualization?