Skip to main content

React Performance Optimization Techniques

Created: March 9, 2026 CalmOps 4 min read

Introduction

React applications can become slow as they grow. Understanding performance optimization techniques helps you build fast, responsive applications. This guide covers essential React performance strategies.

Understanding React Rendering

How React Renders

React creates a virtual DOM and compares changes before updating the real DOM. Excessive or unnecessary rendering causes performance issues.

When Components Re-render

  • Parent re-renders
  • State changes
  • Context changes
  • Props change

Memoization

React.memo

Memoizes components to prevent unnecessary re-renders:

const Button = React.memo(({ onClick, children }) => {
  return <button onClick={onClick}>{children}</button>;
});

useMemo

Memoizes expensive calculations:

function ExpensiveComponent({ data }) {
  const processed = useMemo(() => {
    return data.map(item => ({
      ...item,
      value: heavyComputation(item.value)
    }));
  }, [data]);

  return <List items={processed} />;
}

useCallback

Memoizes functions to prevent unnecessary re-renders:

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return <Child onClick={handleClick} />;
}

Code Splitting

Dynamic Imports

const LazyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <LazyComponent />
    </Suspense>
  );
}

Route-Based Splitting

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/dashboard" element={<Suspense fallback={<Loading/>}><Dashboard/></Suspense>} />
      <Route path="/settings" element={<Suspense fallback={<Loading/>}><Settings/></Suspense>} />
    </Routes>
  );
}

Virtualization

Rendering Large Lists

import { FixedSizeList } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>{items[index].name}</div>
  );

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

Libraries

  • react-window: Lightweight virtualization
  • react-virtualized: More features
  • tanstack virtual: Modern, flexible

Optimization Tools

React DevTools Profiler

import { Profiler } from 'react';

function onRender(id, phase, actualDuration) {
  console.log(`${id} ${phase}: ${actualDuration}ms`);
}

<Profiler id="MyComponent" onRender={onRender}>
  <MyComponent />
</Profiler>

useWhyDidYouUpdate

function useWhyDidYouUpdate(name, props) {
  const previousProps = useRef(props);

  useEffect(() => {
    const keys = Object.keys(props);
    const changes = keys.filter(key => 
      props[key] !== previousProps.current[key]
    );
    
    if (changes.length > 0) {
      console.log(`${name} changed:`, changes);
    }
    
    previousProps.current = props;
  });
}

State Management

Optimizing Context

// Split contexts to avoid unnecessary re-renders
const UserContext = createContext();
const ThemeContext = createContext();

function App() {
  return (
    <UserContext.Provider value={user}>
      <ThemeContext.Provider value={theme}>
        <Component />
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
}

State Colocation

// Bad: State at the top
function App() {
  const [count, setCount] = useState(0);
  const [theme, setTheme] = useState('dark');
  return <Everything count={count} theme={theme} />;
}

// Good: State co-located
function Counter() {
  const [count, setCount] = useState(0);
  return <Display count={count} onIncrement={() => setCount(c => c + 1)} />;
}

function ThemeToggle() {
  const [theme, setTheme] = useState('dark');
  return <Button onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')} />;
}

Bundle Optimization

Analyzing Bundles

# Webpack bundle analyzer
npm install --save-dev webpack-bundle-analyzer

# Next.js
next build --analyze

Reducing Bundle Size

// Instead of lodash
import { cloneDeep, merge } from 'lodash-es';

// Use specific imports
import cloneDeep from 'lodash/cloneDeep';

// Or use alternatives
import { pick } from 'lodash';
// vs
import pick from 'lodash/pick';

Performance Patterns

Callback Patterns

// Stable callbacks
const handleClick = useEventCallback((id) => {
  // Handle click
});

// Custom hook for event callbacks
function useEventCallback(fn, dependencies) {
  const ref = useRef(() => {
    throw new Error('Cannot call event handler while rendering');
  });

  useEffect(() => {
    ref.current = fn;
  }, [fn, ...dependencies]);

  return useCallback((...args) => {
    ref.current(...args);
  }, [ref]);
}

Deferred Values

function SearchResults({ query }) {
  const [deferredQuery, setDeferredQuery] = useState(query);

  useEffect(() => {
    setDeferredQuery(query);
  }, [query]);

  // DeferredValue ensures typing doesn't block rendering
  const deferred = useDeferredValue(deferredQuery);

  return <SlowList query={deferred} />;
}

Measuring Performance

Core Web Vitals

  • LCP: Largest Contentful Paint
  • FID: First Input Delay
  • CLS: Cumulative Layout Shift

Custom Metrics

function measureRenderTime(name, Component) {
  return function MeasuredComponent(props) {
    const start = performance.now();
    const result = renderToReactTree(<Component {...props} />);
    const end = performance.now();
    
    console.log(`${name} took ${end - start}ms`);
    
    return result;
  };
}

Common Mistakes

Over-Memoization

Not everything needs memoization:

// Unnecessary - simple calculations
const total = a + b;

// Only memoize expensive operations
const expensive = useMemo(() => {
  return heavyCalculation(data);
}, [data]);

Premature Optimization

Profile first, optimize second:

  1. Identify the bottleneck
  2. Measure the impact
  3. Optimize strategically

Conclusion

Performance optimization requires understanding React’s rendering model. Use profiling tools, identify real bottlenecks, and apply targeted optimizations. Remember: don’t optimize prematurely.


Resources

Comments

Share this article

Scan to read on mobile