DEV Community

Cover image for React Performance Optimization: 10 Proven Techniques
Umesh Malik
Umesh Malik

Posted on • Edited on • Originally published at umesh-malik.com

React Performance Optimization: 10 Proven Techniques

After optimizing React applications across fintech, automotive, and travel domains, I've identified the techniques that deliver the biggest performance wins. Here are 10 proven optimization strategies.

1. React.memo for Component Memoization

Wrap components that receive the same props frequently to prevent unnecessary re-renders.

const ExpensiveList = React.memo(({ items }: { items: Item[] }) => {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
});
Enter fullscreen mode Exit fullscreen mode

2. useMemo for Expensive Computations

Cache the results of expensive calculations.

function Dashboard({ transactions }: Props) {
  const totalRevenue = useMemo(
    () => transactions.reduce((sum, t) => sum + t.amount, 0),
    [transactions]
  );

  return <span>{totalRevenue}</span>;
}
Enter fullscreen mode Exit fullscreen mode

3. useCallback for Stable References

Prevent child re-renders caused by new function references.

function ParentComponent() {
  const handleClick = useCallback((id: string) => {
    // handle click
  }, []);

  return ;
}
Enter fullscreen mode Exit fullscreen mode

4. Code Splitting with React.lazy

Load components only when they're needed.

const HeavyChart = lazy(() => import('./HeavyChart'));

function Analytics() {
  return (
    Loading...</div>}>


  );
}
Enter fullscreen mode Exit fullscreen mode

5. Virtualize Long Lists

Render only visible items for large datasets.

import { FixedSizeList } from 'react-window';

function UserList({ users }: { users: User[] }) {
  return (

      {({ index, style }) => (
        <div style={style}>{users[index].name}</div>
      )}

  );
}
Enter fullscreen mode Exit fullscreen mode

6. Debounce User Input

Prevent excessive re-renders from rapid input changes.

  • Delay network-bound or filtering work by roughly 150-300ms
  • Pair debouncing with AbortController for fetch-heavy interactions
  • Avoid debouncing the visible input state itself

7. Optimize Context Usage

Split contexts to prevent unnecessary re-renders across the component tree.

  • Keep auth, theme, permissions, and feature flags in separate contexts when practical
  • Memoize provider values so consumers don't churn on every render
  • Reach for selector patterns before introducing new state libraries

8. Use the key Prop Strategically

Force component remounting when data changes fundamentally.

  • Reset a form when userId or recordId changes
  • Remount charts when the data shape changes fundamentally
  • Don't use keys to hide deeper state management bugs

9. Lazy Load Images

Use the native loading="lazy" attribute or Intersection Observer.

  • Add width and height or aspect-ratio to avoid layout shifts
  • Use eager loading and fetchpriority="high" only for true hero media
  • Prefer responsive srcset over a single oversized asset

10. Profile with React DevTools

Always measure before optimizing. Use the React Profiler to identify actual bottlenecks.

  • Capture the exact interaction that feels slow
  • Compare flame charts before and after each change
  • Re-test on lower-end hardware assumptions, not just your laptop

Key Takeaways

  • Always measure performance before optimizing
  • Focus on the techniques that address your specific bottlenecks
  • React.memo and useMemo are your most-used tools
  • Code splitting has the biggest impact on initial load time
  • Virtualization is essential for large datasets

These techniques have helped me build applications processing millions of transactions with smooth, responsive UIs.


Originally published at umesh-malik.com

Top comments (0)