Performance

5 Common React Performance Mistakes Even Experienced Developers Make

5/9/2025 • 10 min read
Key Takeaways
  • Unnecessary re-renders account for ~60% of React performance issues
  • Simple memoization can improve performance by 3-5x
  • Bundle size impacts initial load more than runtime perf

React's declarative nature makes it easy to write code, but this simplicity often hides performance pitfalls. After auditing dozens of production React apps, I've identified these recurring issues.

1. Not Memoizing Expensive Calculations

Every render recalculates values in your component body. For complex operations, this wastes CPU cycles and can cause noticeable lag.

// ❌ Recalculates on every render
const sortedList = hugeArray.sort(complexSort);

return <List items={sortedList} />;

2. Inline Function Creation in Render

New function references on each render break memoization and cause child components to re-render unnecessarily.

Problem

<Child onClick={() => handleClick(id)} />
Creates new function on every render

Solution

const handleChildClick = useCallback(
  () => handleClick(id),
  [id]
);

<Child onClick={handleChildClick} />
Stable function reference

3. Overusing State for Static Data

Using useState for data that never changes triggers unnecessary re-renders and consumes memory.

Common culprits: Configuration objects, constant lists, environment variables

// ❌ Unnecessary state
const [config] = useState({ apiUrl: '...', theme: 'dark' });

// ✅ Better alternatives:
const config = { apiUrl: '...', theme: 'dark' }; // Plain object
const config = useRef({ apiUrl: '...', theme: 'dark' }).current; // Ref
const config = useMemo(() => ({ apiUrl: '...', theme: 'dark' }), []);

4. Not Virtualizing Long Lists

Rendering hundreds/thousands of list items DOM nodes cripples performance.

Solution: Use windowing libraries like react-window or react-virtualized

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const MyList = () => (
  <List
    height={500}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);

5. Ignoring Bundle Size

Large JavaScript bundles delay interactive time, especially on mobile.

  • Use code splitting with React.lazy

    Splits your app into smaller chunks

  • Analyze bundles with source-map-explorer

    Identifies heavy dependencies

  • Use tree-shakeable libraries

    Like lodash-es instead of lodash

Example of code splitting routes:

// Before
import HeavyComponent from './HeavyComponent';

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

// Usage with Suspense
<Suspense fallback={<Spinner />}>
  <HeavyComponent />
</Suspense>

Ready to Audit Your App?

Use these tools to identify performance issues:

React DevTools

Component profiling

Chrome Lighthouse

Performance metrics

Why Did You Render

Re-render tracking

Webpack Bundle Analyzer

Bundle size visualization

Explore Performance Optimization Roadmap