React

Performance

Optimize React application performance with memoization, code splitting, virtualization, and profiling.

React.memo & Component Memoization

React.memo prevents a component from re-rendering when its props haven't changed. Use it for expensive components that receive the same props frequently.

import React, { useState, memo, useCallback } from 'react';

// Expensive list item - only re-render when 'item' prop changes
const ListItem = memo(function ListItem({
  item,
  onDelete,
}: {
  item: { id: number; text: string };
  onDelete: (id: number) => void;
}) {
  console.log(`Rendering: ${item.text}`);

  return (
    <li className="flex justify-between items-center p-3">
      <span>{item.text}</span>
      <button onClick={() => onDelete(item.id)}>Delete</button>
    </li>
  );
});

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: "Learn React" },
    { id: 2, text: "Build something" },
  ]);
  const [input, setInput] = useState('');

  // useCallback ensures same function reference for memo to work
  const handleDelete = useCallback((id: number) => {
    setTodos(prev => prev.filter(t => t.id !== id));
  }, []);

  return (
    <div>
      <ul>
        {todos.map(todo => (
          <ListItem key={todo.id} item={todo} onDelete={handleDelete} />
        ))}
      </ul>
      <input value={input} onChange={e => setInput(e.target.value)} />
    </div>
  );
}

Lazy Loading & Code Splitting

Split your bundle into smaller chunks loaded on demand. This reduces initial page load time dramatically.

import { lazy, Suspense } from 'react';
import dynamic from 'next/dynamic';

// React.lazy for client-side code splitting
const HeavyChart = lazy(() => import('./HeavyChart'));
const MarkdownEditor = lazy(() => import('./MarkdownEditor'));

// Next.js dynamic import with options
const VideoPlayer = dynamic(() => import('./VideoPlayer'), {
  loading: () => <div className="animate-pulse bg-gray-800 h-64 rounded-xl" />,
  ssr: false, // Don't render on server (browser-only component)
});

function Dashboard() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <button onClick={() => setShowChart(true)}>Load Chart</button>

      {showChart && (
        // Suspense provides loading UI while lazy component loads
        <Suspense fallback={<div>Loading chart...</div>}>
          <HeavyChart data={[1, 2, 3]} />
        </Suspense>
      )}

      {/* VideoPlayer loaded client-side only */}
      <VideoPlayer src="/video.mp4" />
    </div>
  );
}

Profiling & Measuring Performance

Before optimizing, measure. Use React DevTools Profiler and the built-in Profiler component to identify bottlenecks.

import { Profiler, ProfilerOnRenderCallback } from 'react';

const onRender: ProfilerOnRenderCallback = (
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime
) => {
  // Log slow renders (>16ms = below 60fps)
  if (actualDuration > 16) {
    console.warn(`Slow render in "${id}": ${actualDuration.toFixed(2)}ms`);
  }
};

function App() {
  return (
    <Profiler id="MainContent" onRender={onRender}>
      <MainContent />
    </Profiler>
  );
}

// Use Web Vitals for real-world metrics
export function reportWebVitals(metric: NextWebVitalsMetric) {
  switch (metric.name) {
    case 'LCP': // Largest Contentful Paint
    case 'FCP': // First Contentful Paint
    case 'CLS': // Cumulative Layout Shift
    case 'FID': // First Input Delay
    case 'TTFB': // Time To First Byte
      console.log(metric);
      break;
  }
}

Target: LCP < 2.5s, FID < 100ms, CLS < 0.1. These are Google's Core Web Vitals thresholds for "Good" performance.