Software

React Performance Optimization: Advanced Techniques

Optimize React applications for speed. Master code splitting, memoization, virtualization, and Core Web Vitals improvements.

All articles
SoftwareNexaEx TeamMarch 30, 2026 9 min read
React Performance Optimization: Advanced Techniques

React Performance Challenges

React applications often suffer performance issues: large bundle sizes, unnecessary re-renders, janky animations. Performance directly impacts user satisfaction and conversion rates.

Code Splitting and Lazy Loading

Bundle everything in one file? Users download unnecessary code. Code splitting breaks bundles into chunks loaded on demand.

import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));
const Admin = lazy(() => import('./Admin'));

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

Users visiting dashboard load Dashboard chunk. Admin users load Admin chunk. Initial bundle shrinks 30-50%.

Route-based splitting for page-level components. Component-based splitting for heavy components (modals, datepickers).

Memoization and Preventing Re-renders

By default, React re-renders on every state change. Components receiving props re-render even if props unchanged.

// Without memo - re-renders on every parent render
function UserCard({ user }) {
  return <div>{user.name}</div>;
}

// With memo - re-renders only if user prop changes
const UserCard = memo(function UserCard({ user }) {
  return <div>{user.name}</div>;
}, (prev, next) => prev.user.id === next.user.id);

Memoization prevents re-renders but costs performance (comparison checks). Memo complex components rendering often. Skip memo on simple components.

useMemo prevents expensive computations:

const expensiveValue = useMemo(() => {
  return fibonacci(50); // Expensive
}, [dependency]);

useCallback prevents function recreation:

const handleClick = useCallback(() => {
  dispatch(action);
}, [dispatch]);

Virtual Scrolling for Large Lists

Rendering 10,000 list items creates 10,000 DOM nodes. Browsers struggle. Virtual scrolling renders only visible items.

import { FixedSizeList } from 'react-window';

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

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

10,000 items, 40 visible. Only 40 DOM nodes. Scrolling stays smooth.

Image Optimization

Images are often the largest assets. Optimize aggressively:

import Image from 'next/image';

export function Hero() {
  return (
    <Image
      src="/hero.jpg"
      alt="Hero"
      width={1200}
      height={600}
      quality={75}
      placeholder="blur"
      blurDataURL={blurredBase64}
    />
  );
}

Next.js Image component:

  • Serves optimized formats (WebP)
  • Lazy loads below fold
  • Responsive sizing
  • Built-in blur placeholder

Bundle Size Analysis

Identify large dependencies:

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

Analyze output. Replace large dependencies:

  • moment → date-fns (100kb → 10kb)
  • lodash → lodash-es (70kb → 4kb tree-shaken)
  • axios → fetch (reducing http client)

Core Web Vitals Optimization

Google ranks sites on Core Web Vitals:

LCP (Largest Contentful Paint): Load time for largest visible element. Target <2.5s.

  • Optimize images
  • Defer non-critical CSS
  • Preload critical fonts

FID (First Input Delay): Time to respond to user input. Target <100ms.

  • Reduce main thread blocking work
  • Break up long tasks with setTimeout

CLS (Cumulative Layout Shift): Unexpected layout changes. Target <0.1.

  • Reserve space for images
  • Avoid inserting content above existing
// Prevent layout shift
<div className="card" style={{ aspectRatio: '16/9' }}>
  <Image
    src={...}
    fill
    sizes="100%"
  />
</div>

Profiling and Measurement

Use React DevTools Profiler:

  • Record component renders
  • Identify slow components
  • Check if memoization helps

Use Lighthouse in Chrome:

  • Audit performance
  • Get specific recommendations
  • Track improvements over time

Frequently asked questions

When should we use code splitting vs keeping code in main bundle?

Split routes and heavy modals. Keep utility libraries in main bundle (always needed). Rule: if feature used by <30% of users or loads >50kb, split it. Main bundle target: <100kb gzip.

How much overhead does memoization add?

Memo comparison ~0.1-1ms per component. Benefits appear when render time >5ms or renders frequent (hundreds/second). Don't memo simple components. Profile first, optimize based on data.

How do we handle dynamic data with virtual scrolling?

Virtual scrolling works with pagination or windowed loading. Load next 100 items as user scrolls near end. Infinite scroll with placeholders while loading. react-window supports dynamic sizing per item.

Let's build your next idea

One conversation to scope the work, meet the team, and get a proposal — usually within two business days.