skip to content
logo
Table of Contents

Improving React Performance

Performance issues in React apps usually arise from unnecessary re-renders, inefficient state management, and large component trees.
This guide covers:

  • Avoiding unnecessary re-renders
  • Optimizing state management
  • Using memoization and lazy loading
  • Implementing virtualization for large lists

Avoiding Unnecessary Re-Renders

Excessive re-renders slow down React applications. Use React.memo, useCallback, and useMemo to optimize renders.

Using React.memo to Prevent Re-Renders

If a component receives the same props but still re-renders, wrap it with React.memo.

import React from "react";
type ButtonProps = {
text: string;
onClick: () => void;
};
const Button = React.memo(({ text, onClick }: ButtonProps) => {
console.log("Button rendered");
return <button onClick={onClick}>{text}</button>;
});

Using useCallback to Prevent Unnecessary Function Re-Creation

In child components, passing a new function reference on every render causes unnecessary renders.
Wrap event handlers in useCallback.

import React, { useState, useCallback } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount((prev) => prev + 1), []);
return <button onClick={increment}>Count: {count}</button>;
};

Using useMemo for Expensive Computations

Use useMemo to prevent expensive calculations from running on every render.

import React, { useState, useMemo } from "react";
const HeavyComponent = ({ number }: { number: number }) => {
const expensiveCalculation = useMemo(() => {
console.log("Expensive function running");
return number ** 2;
}, [number]);
return <p>Result: {expensiveCalculation}</p>;
};

Optimizing State Management

Avoid prop drilling and excessive re-renders by using global state managers or local component state when appropriate.

Using useContext with useMemo

Avoid unnecessary re-renders when passing context values.

import React, { createContext, useContext, useState, useMemo } from "react";
const ThemeContext = createContext({ theme: "light", toggleTheme: () => {} });
const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
const [theme, setTheme] = useState("light");
const value = useMemo(() => ({
theme,
toggleTheme: () => setTheme((prev) => (prev === "light" ? "dark" : "light")),
}), [theme]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
};
const useTheme = () => useContext(ThemeContext);

Virtualizing Large Lists

Rendering a long list slows down performance. Use React Virtualized or react-window to only render visible elements.

import { FixedSizeList as List } from "react-window";
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style}>Item {index}</div>
);
const VirtualizedList = () => (
<List height={500} itemCount={1000} itemSize={35} width={300}>
{Row}
</List>
);

Lazy Loading Components

Load components only when needed using React.lazy and Suspense.

import React, { Suspense, lazy } from "react";
const HeavyComponent = lazy(() => import("./HeavyComponent"));
const App = () => (
<Suspense fallback={<p>Loading...</p>}>
<HeavyComponent />
</Suspense>
);

Conclusion

  • Use React.memo, useCallback, and useMemo to prevent re-renders.
  • Optimize state management with useContext and useReducer.
  • Use virtualization for large lists to reduce DOM rendering overhead.
  • Implement lazy loading to reduce initial bundle size.

Performance tuning in React requires a thoughtful approach to re-renders, state, and data flow. Applying these techniques will make your React app more efficient.