My App
React

React Performance Patterns — Optimization Techniques with Real World Examples

Learn how to make React apps faster using React.memo, useMemo, useCallback, code-splitting, and bundle optimization — with simple logic and real-world examples.

⚛️ React Performance Patterns

Make your React applications faster and smoother with smart optimization techniques.


🧠 Why React Performance Matters

React is fast, but poor component design and unnecessary re-renders can make even small apps slow.
Performance optimization means reducing unnecessary renders, optimizing computations, and loading code only when needed.

We’ll cover:

  • React.memo and Component Memoization
  • useMemo and useCallback
  • Code splitting with React.lazy
  • Bundle Analysis
  • Virtual DOM performance

Each explained simply — with real-world logic and working code.


🧩 1. React.memo and Component Memoization

🔹 What It Does

React.memo prevents a component from re-rendering unless its props change.

By default, React re-renders child components whenever the parent updates — even if props stay the same.
Memoization helps skip those re-renders, saving time.


🔹 Simple Example

import React from "react";

const TaskItem = React.memo(function TaskItem({ title }) {
  console.log("Rendering:", title);
  return <li>{title}</li>;
});

export default function TaskList({ tasks }) {
  return (
    <ul>
      {tasks.map((task) => (
        <TaskItem key={task.id} title={task.title} />
      ))}
    </ul>
  );
}

Here, TaskItem only re-renders when its title prop changes.

💡 Real-World Example: In a task tracker app, only the edited task should update — not every item. React.memo avoids re-rendering all tasks, improving speed as lists grow.


🔹 When to Use

✅ Use React.memo for:

  • Lists of items
  • Heavy UI components
  • Components with expensive rendering logic

❌ Avoid it for tiny, fast-rendering components — memoization itself has a cost.


⚙️ 2. useMemo and useCallback Optimization

🔹 Why We Use Them

  • useMemo: Caches computed values
  • useCallback: Caches function references

They prevent React from recalculating or recreating the same things during every render.


🔹 Example: useMemo

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

export default function PriceCalculator() {
  const [items, setItems] = useState([10, 20, 30]);
  const [discount, setDiscount] = useState(10);

  const total = useMemo(() => {
    console.log("Calculating total...");
    return items.reduce((a, b) => a + b, 0) - discount;
  }, [items, discount]);

  return (
    <>
      <h3>Total: {total}</h3>
      <button onClick={() => setDiscount(discount + 5)}>Increase Discount</button>
    </>
  );
}

💡 Real-World Example: An e-commerce checkout page can use useMemo to compute cart totals only when items or discounts change, not every render.


🔹 Example: useCallback

import React, { useCallback, useState } from "react";

function Button({ onClick, label }) {
  console.log("Rendering:", label);
  return <button onClick={onClick}>{label}</button>;
}

export default function Counter() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => setCount((c) => c + 1), []);
  const decrement = useCallback(() => setCount((c) => c - 1), []);

  return (
    <>
      <h3>Count: {count}</h3>
      <Button onClick={increment} label="Increment" />
      <Button onClick={decrement} label="Decrement" />
    </>
  );
}

Here, useCallback ensures that increment and decrement are not recreated on every render — so the Button component (wrapped in React.memo) doesn’t re-render unnecessarily.

💡 Real-World Example: In data dashboards, you can use useCallback for filter buttons so that the chart doesn’t re-render when other UI parts update.


🔹 When to Use

HookUse WhenExample
useMemoComputation is heavyPrice calculation, data sorting
useCallbackFunction passed to childrenButtons, input handlers

⚡ 3. Code Splitting with React.lazy

🔹 What It Does

Code splitting means dividing your app into smaller chunks so the browser loads only what’s needed — improving load time.


🔹 Example

import React, { Suspense, lazy } from "react";

const Reports = lazy(() => import("./Reports"));
const Dashboard = lazy(() => import("./Dashboard"));

export default function App() {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <Dashboard />
      <Reports />
    </Suspense>
  );
}

💡 Real-World Example: When you open YouTube, it first loads the home feed quickly — while heavy pages like “Settings” or “Upload” load only when you click them. That’s code-splitting in action.


🔹 Tools to Help

  • React.lazy and Suspense for component-level loading
  • dynamic import() for route-level code splitting
  • Build-time optimization with Webpack or Vite

📦 4. Bundle Analysis and Optimization

Large bundles slow down your app — especially on mobile. Bundle analysis helps identify unnecessary or duplicate code.


🔸 Step 1: Analyze Bundle Size

For Create React App:

npm run build
npx source-map-explorer "build/static/js/*.js"

For Vite:

npm run build
npx vite-bundle-visualizer

You’ll see which libraries take the most space (like React, Lodash, or Moment.js).


🔸 Step 2: Optimize

ProblemSolution
Big librariesReplace with smaller alternatives (e.g., dayjs instead of moment.js)
Unused importsRemove or lazy-load components
Repeated dependenciesDeduplicate via build tools
Large imagesCompress and use WebP

💡 Real-World Example: When Netflix optimized their React bundles, they cut initial load size by 30%, making the homepage load nearly twice as fast on slow networks.


🌳 5. Virtual DOM Performance Considerations

🔹 How React’s Virtual DOM Works

React uses a Virtual DOM — a lightweight copy of the real DOM. When state changes:

  1. React builds a new Virtual DOM tree.
  2. It compares (diffs) it with the old one.
  3. Only the changed parts update in the real DOM.

🔹 Why Performance Can Drop

  1. Too many re-renders (no memoization)
  2. Large component trees
  3. Inline functions recreated each render
  4. Unnecessary re-renders from parent updates

🔹 Optimization Techniques

ProblemFix
Frequent re-rendersUse React.memo, useMemo, useCallback
Heavy DOM updatesSplit components into smaller parts
Expensive calculationsUse useMemo or move to background
Repeated props passingUse Context or global store like Zustand or Redux

💡 Real-World Example: In Slack’s web app, typing messages caused slowdowns because all components re-rendered with every keystroke. By using React.memo and splitting components, they improved typing performance by 40%.


🧾 Summary

ConceptWhat It DoesReal-World Example
React.memoPrevents unnecessary re-rendersTask Tracker list rendering
useMemoCaches expensive calculationsE-commerce total price
useCallbackCaches functionsDashboard filter buttons
React.lazySplits bundles for faster loadYouTube and Netflix modules
Bundle AnalysisFinds heavy filesNetflix reduced load size
Virtual DOMEfficient rendering systemSlack typing performance boost

💡 Final Thought

Performance in React is all about smart rendering and efficient loading. You don’t need to optimize everything — only what’s slow or frequently re-rendered.

A well-optimized React app:

  • Loads faster
  • Uses less memory
  • Feels smoother

As apps scale, these patterns help your frontend stay as responsive as the world’s best — like Netflix, Slack, and YouTube.