React Performance Optimization

Optimize your React apps with memoization, code splitting, virtualization, and profiling. Learn to identify and fix performance bottlenecks.

Performance optimization in React is about minimizing unnecessary work. React is fast by default, but complex apps may need optimization.

Measuring Performance

Use React DevTools Profiler and browser performance tools to identify bottlenecks before optimizing. Premature optimization is counterproductive.

Memoization

React.memo, useMemo, and useCallback prevent unnecessary recalculations and re-renders. Use them when you have expensive computations or stable callback references.

Code Splitting

Reduce initial bundle size with dynamic imports and React.lazy. Load components only when needed.

Virtualization

For long lists, render only visible items using libraries like react-window or TanStack Virtual.

Keys and Reconciliation

Proper key usage helps React efficiently update lists. Never use array indices as keys if the list can reorder.

Code Examples

Memoization

MemoExample.jsx
import { memo, useMemo, useCallback } from 'react';

// Memoized component - only re-renders if props change
const ExpensiveList = memo(function ExpensiveList({ items, onItemClick }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id} onClick={() => onItemClick(item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  );
});

function Parent({ data }) {
  const [filter, setFilter] = useState('');

  // Memoize expensive computation
  const filteredItems = useMemo(() => {
    return data.filter(item =>
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [data, filter]);

  // Memoize callback to maintain reference
  const handleItemClick = useCallback((id) => {
    console.log('Clicked:', id);
  }, []);

  return (
    <>
      <input
        value={filter}
        onChange={e => setFilter(e.target.value)}
        placeholder="Filter..."
      />
      <ExpensiveList
        items={filteredItems}
        onItemClick={handleItemClick}
      />
    </>
  );
}

Code Splitting

LazyRoutes.jsx
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

// Lazy load route components
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() =>
  import('./pages/Analytics').then(module => ({
    default: module.Analytics // Named export
  }))
);

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/analytics" element={<Analytics />} />
      </Routes>
    </Suspense>
  );
}

// Loading component
function LoadingSpinner() {
  return (
    <div className="loading">
      <span>Loading...</span>
    </div>
  );
}

Virtualized List

VirtualList.jsx
import { FixedSizeList } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style} className="list-row">
      <span>{items[index].name}</span>
      <span>{items[index].email}</span>
    </div>
  );

  return (
    <FixedSizeList
      height={400}
      width="100%"
      itemCount={items.length}
      itemSize={50}
    >
      {Row}
    </FixedSizeList>
  );
}

// For variable height items
import { VariableSizeList } from 'react-window';

function DynamicList({ items }) {
  const getItemSize = (index) => {
    // Return height based on content
    return items[index].expanded ? 100 : 50;
  };

  return (
    <VariableSizeList
      height={400}
      width="100%"
      itemCount={items.length}
      itemSize={getItemSize}
    >
      {({ index, style }) => (
        <div style={style}>{items[index].content}</div>
      )}
    </VariableSizeList>
  );
}

Frequently Asked Questions

When should I use React.memo?

Use memo when a component re-renders with the same props frequently, especially if rendering is expensive. Don't use it everywhere - the comparison has its own cost. Profile first.

Why does my component re-render even with memo?

Common causes: creating new object/array/function references in props, context changes, or parent state updates. Check if callbacks are memoized with useCallback and objects with useMemo.

How do I know if I need to optimize?

Use React DevTools Profiler to measure. Look for components that render frequently with the same props, long render times (>16ms), or janky interactions. Optimize only verified bottlenecks.

Need React Help?

Slashdev.io builds production-ready React applications for businesses of all sizes.

Get in Touch