React Performance Monitoring and Optimization

1. React Profiler API and Performance Metrics

Feature API Metrics Captured Use Case
Profiler Component <Profiler id="..." onRender={...}> Render time, phase, actual duration Component-level performance tracking
onRender Callback onRender(id, phase, actualDuration, ...) All render metrics per component tree Collect performance data programmatically
Interaction Tracing DevTools Profiler with interactions User interactions causing renders Debug performance bottlenecks
Commit Phase Timing baseDuration vs actualDuration Optimization impact measurement Measure memo/callback effectiveness
Custom Metrics Performance API integration Custom timing, user timing marks Business-specific metrics
Production Profiling React with profiling build Real user performance data Production performance monitoring

Example: React Profiler API usage

import { Profiler, ProfilerOnRenderCallback } from 'react';

// Profiler callback
const onRenderCallback: ProfilerOnRenderCallback = (
  id,                    // Profiler id
  phase,                 // "mount" or "update"
  actualDuration,        // Time spent rendering
  baseDuration,          // Estimated time without memoization
  startTime,             // When React began rendering
  commitTime,            // When React committed the update
  interactions           // Set of interactions for this update
) => {
  console.log(`Profiler [${id}] - ${phase}`);
  console.log(`Actual duration: ${actualDuration}ms`);
  console.log(`Base duration: ${baseDuration}ms`);
  console.log(`Time saved: ${baseDuration - actualDuration}ms`);
  
  // Send to analytics
  if (actualDuration > 16) { // More than one frame
    sendToAnalytics({
      component: id,
      phase,
      duration: actualDuration,
      timestamp: commitTime,
    });
  }
};

// Wrap component tree with Profiler
function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <Header />
      <Profiler id="Navigation" onRender={onRenderCallback}>
        <Navigation />
      </Profiler>
      <Profiler id="Content" onRender={onRenderCallback}>
        <MainContent />
      </Profiler>
      <Footer />
    </Profiler>
  );
}

// Custom performance tracking hook
function usePerformanceTracking(componentName: string) {
  useEffect(() => {
    const startMark = `${componentName}-start`;
    const endMark = `${componentName}-end`;
    const measureName = `${componentName}-render`;

    performance.mark(startMark);

    return () => {
      performance.mark(endMark);
      performance.measure(measureName, startMark, endMark);
      
      const measure = performance.getEntriesByName(measureName)[0];
      console.log(`${componentName} render time: ${measure.duration}ms`);
      
      // Clean up
      performance.clearMarks(startMark);
      performance.clearMarks(endMark);
      performance.clearMeasures(measureName);
    };
  });
}

// Usage in component
function ExpensiveComponent() {
  usePerformanceTracking('ExpensiveComponent');
  
  // Component logic
  return <div>...</div>;
}

Example: Production performance monitoring

// Performance monitoring service
class PerformanceMonitor {
  private metrics: Map<string, number[]> = new Map();

  recordMetric(name: string, duration: number) {
    if (!this.metrics.has(name)) {
      this.metrics.set(name, []);
    }
    this.metrics.get(name)!.push(duration);
  }

  getStats(name: string) {
    const durations = this.metrics.get(name) || [];
    if (durations.length === 0) return null;

    const sorted = [...durations].sort((a, b) => a - b);
    return {
      count: durations.length,
      avg: durations.reduce((a, b) => a + b, 0) / durations.length,
      median: sorted[Math.floor(sorted.length / 2)],
      p95: sorted[Math.floor(sorted.length * 0.95)],
      p99: sorted[Math.floor(sorted.length * 0.99)],
      min: sorted[0],
      max: sorted[sorted.length - 1],
    };
  }

  flush() {
    const stats: Record<string, any> = {};
    this.metrics.forEach((_, name) => {
      stats[name] = this.getStats(name);
    });
    
    // Send to backend
    fetch('/api/performance', {
      method: 'POST',
      body: JSON.stringify(stats),
    });
    
    this.metrics.clear();
  }
}

const monitor = new PerformanceMonitor();

// Global profiler callback
const globalProfilerCallback: ProfilerOnRenderCallback = (
  id,
  phase,
  actualDuration
) => {
  monitor.recordMetric(`${id}-${phase}`, actualDuration);
};

// Flush metrics periodically
setInterval(() => {
  monitor.flush();
}, 60000); // Every minute

// Web Vitals integration
import { onCLS, onFID, onLCP, onFCP, onTTFB } from 'web-vitals';

function sendToAnalytics({ name, delta, id }: any) {
  fetch('/api/vitals', {
    method: 'POST',
    body: JSON.stringify({ name, value: delta, id }),
  });
}

onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

2. Bundle Analysis and Code Splitting Strategies

Strategy Implementation Bundle Impact Best For
Route-based Splitting React.lazy(() => import('./Route')) Split by page/route Multi-page applications
Component-based Splitting Lazy load heavy components Split by feature/component Large components, modals, charts
Library Splitting Dynamic imports for large libs Vendor bundle optimization Heavy libraries (PDF, charts, maps)
Webpack Bundle Analyzer webpack-bundle-analyzer Visualize bundle composition Identify optimization opportunities
Tree Shaking ES6 modules, side-effect-free Remove unused exports Reduce bundle size automatically
Prefetching import(/* webpackPrefetch: true */) Load during idle time Improve perceived performance

Example: Advanced code splitting patterns

// Route-based code splitting
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));

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

// Component-based splitting with prefetch
function ProductList() {
  const [showModal, setShowModal] = useState(false);
  
  // Lazy load modal component
  const ProductModal = lazy(() => import('./ProductModal'));

  // Prefetch on hover
  const handleMouseEnter = () => {
    import('./ProductModal'); // Prefetch
  };

  return (
    <div>
      <button 
        onClick={() => setShowModal(true)}
        onMouseEnter={handleMouseEnter}
      >
        View Details
      </button>
      
      {showModal && (
        <Suspense fallback={<Spinner />}>
          <ProductModal onClose={() => setShowModal(false)} />
        </Suspense>
      )}
    </div>
  );
}

// Library splitting - heavy chart library
const Chart = lazy(() => import('recharts').then(module => ({
  default: module.LineChart
})));

function Analytics() {
  return (
    <Suspense fallback={<ChartSkeleton />}>
      <Chart data={data} />
    </Suspense>
  );
}

// Dynamic import with error handling
function DynamicComponent() {
  const [Component, setComponent] = useState<React.ComponentType | null>(null);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    import('./HeavyComponent')
      .then(module => setComponent(() => module.default))
      .catch(err => setError(err));
  }, []);

  if (error) return <ErrorMessage error={error} />;
  if (!Component) return <Loading />;

  return <Component />;
}

// Webpack magic comments for optimization
const AdminPanel = lazy(() =>
  import(
    /* webpackChunkName: "admin" */
    /* webpackPrefetch: true */
    './pages/AdminPanel'
  )
);

const ReportGenerator = lazy(() =>
  import(
    /* webpackChunkName: "reports" */
    /* webpackPreload: true */
    './components/ReportGenerator'
  )
);

Example: Bundle analysis and optimization

// package.json scripts for bundle analysis
{
  "scripts": {
    "analyze": "webpack-bundle-analyzer build/bundle-stats.json",
    "build:analyze": "npm run build -- --stats && npm run analyze"
  }
}

// Webpack configuration for bundle optimization
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // Vendor bundle
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
        },
        // Common components
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
        // React core
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20,
        },
      },
    },
    runtimeChunk: 'single', // Extract runtime into separate chunk
  },
};

// Analyze imports and suggest optimizations
import { lazy } from 'react';

// ❌ Bad: Import entire library
import _ from 'lodash';
import moment from 'moment';

// ✅ Good: Import only what you need
import debounce from 'lodash/debounce';
import dayjs from 'dayjs'; // Lighter alternative

// ❌ Bad: Import all icons
import * as Icons from 'react-icons/fa';

// ✅ Good: Import specific icons
import { FaUser, FaHome } from 'react-icons/fa';

// Tree shaking configuration in package.json
{
  "sideEffects": [
    "*.css",
    "*.scss"
  ]
}

// Check bundle size with size-limit
// size-limit.config.js
module.exports = [
  {
    name: 'Main bundle',
    path: 'build/static/js/*.js',
    limit: '200 KB',
  },
  {
    name: 'Vendor bundle',
    path: 'build/static/js/vendors.*.js',
    limit: '150 KB',
  },
];

3. Memory Leak Detection and Prevention

Leak Source Detection Method Prevention Tools
Event Listeners Memory snapshots, heap profiling Remove in cleanup function Chrome DevTools Memory Profiler
Timers/Intervals Check running timers count Clear in useEffect cleanup React DevTools Profiler
Subscriptions Monitor active subscriptions Unsubscribe in cleanup Custom logging
DOM References Detached DOM tree analysis Clear refs, remove listeners Chrome Memory Profiler
State Updates Warning: setState on unmounted Cancel async operations React strict mode warnings
Closures Heap snapshot comparison Avoid large closure scopes Memory timeline recording

Example: Memory leak prevention patterns

// ✅ Proper event listener cleanup
function WindowResizeComponent() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    window.addEventListener('resize', handleResize);
    
    // Cleanup: Remove listener
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return <div>Width: {width}</div>;
}

// ✅ Timer cleanup
function CountdownTimer() {
  const [count, setCount] = useState(60);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(c => c - 1);
    }, 1000);

    // Cleanup: Clear interval
    return () => {
      clearInterval(intervalId);
    };
  }, []);

  return <div>{count}s remaining</div>;
}

// ✅ Abort controller for fetch requests
function DataFetcher({ id }: { id: string }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    const abortController = new AbortController();

    async function fetchData() {
      try {
        const response = await fetch(`/api/data/${id}`, {
          signal: abortController.signal,
        });
        const json = await response.json();
        setData(json);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        }
      }
    }

    fetchData();

    // Cleanup: Abort pending request
    return () => {
      abortController.abort();
    };
  }, [id]);

  return <div>{JSON.stringify(data)}</div>;
}

// ✅ WebSocket cleanup
function WebSocketComponent() {
  const [messages, setMessages] = useState<string[]>([]);

  useEffect(() => {
    const ws = new WebSocket('ws://localhost:8080');

    ws.onmessage = (event) => {
      setMessages(prev => [...prev, event.data]);
    };

    // Cleanup: Close connection
    return () => {
      ws.close();
    };
  }, []);

  return <ul>{messages.map((msg, i) => <li key={i}>{msg}</li>)}</ul>;
}

// ✅ Observable/RxJS subscription cleanup
import { interval } from 'rxjs';

function ObservableComponent() {
  const [tick, setTick] = useState(0);

  useEffect(() => {
    const subscription = interval(1000).subscribe(val => {
      setTick(val);
    });

    // Cleanup: Unsubscribe
    return () => {
      subscription.unsubscribe();
    };
  }, []);

  return <div>Tick: {tick}</div>;
}

Example: Memory leak detection utilities

// Custom hook to detect memory leaks
function useMemoryLeakDetector(componentName: string) {
  useEffect(() => {
    const mountTime = Date.now();
    console.log(`[${componentName}] Mounted`);

    return () => {
      const unmountTime = Date.now();
      const lifetime = unmountTime - mountTime;
      console.log(`[${componentName}] Unmounted after ${lifetime}ms`);

      // Check for detached listeners
      if (process.env.NODE_ENV === 'development') {
        setTimeout(() => {
          // This will warn if state updates after unmount
          console.log(`[${componentName}] Cleanup verification`);
        }, 100);
      }
    };
  }, [componentName]);
}

// Safe async state updates
function useSafeState<T>(initialState: T) {
  const [state, setState] = useState(initialState);
  const isMountedRef = useRef(true);

  useEffect(() => {
    return () => {
      isMountedRef.current = false;
    };
  }, []);

  const setSafeState = useCallback((value: T | ((prev: T) => T)) => {
    if (isMountedRef.current) {
      setState(value);
    }
  }, []);

  return [state, setSafeState] as const;
}

// Usage
function AsyncComponent() {
  const [data, setData] = useSafeState(null);

  useEffect(() => {
    fetchData().then(result => {
      setData(result); // Safe even if unmounted
    });
  }, [setData]);

  return <div>{JSON.stringify(data)}</div>;
}

// Memory profiling helper
class MemoryProfiler {
  private snapshots: number[] = [];

  takeSnapshot() {
    if (performance.memory) {
      this.snapshots.push(performance.memory.usedJSHeapSize);
      console.log(`Heap size: ${(performance.memory.usedJSHeapSize / 1048576).toFixed(2)} MB`);
    }
  }

  analyze() {
    if (this.snapshots.length < 2) return;

    const growth = this.snapshots[this.snapshots.length - 1] - this.snapshots[0];
    const avgGrowth = growth / this.snapshots.length;

    console.log(`Total growth: ${(growth / 1048576).toFixed(2)} MB`);
    console.log(`Average growth per snapshot: ${(avgGrowth / 1048576).toFixed(2)} MB`);
  }
}

const profiler = new MemoryProfiler();

// Take snapshots periodically
setInterval(() => profiler.takeSnapshot(), 5000);

4. Re-render Analysis and Optimization

Technique Implementation Impact When to Use
React.memo Wrap component with memo Skip re-render if props unchanged Pure components with expensive renders
useMemo Memoize expensive calculations Cache computed values Heavy computations, derived data
useCallback Memoize function references Stable function identity Callbacks passed to memo'd children
Context Splitting Separate contexts by update frequency Reduce context re-render scope Large context with mixed data
Component Splitting Break into smaller components Isolate re-renders Large components with mixed state
Virtualization Render only visible items Massive performance gain for lists Long lists, tables, grids

Example: Re-render optimization patterns

// ✅ React.memo with custom comparison
interface ItemProps {
  item: { id: string; name: string; price: number };
  onSelect: (id: string) => void;
}

const ListItem = memo(({ item, onSelect }: ItemProps) => {
  console.log(`Rendering item: ${item.id}`);
  
  return (
    <div onClick={() => onSelect(item.id)}>
      {item.name} - ${item.price}
    </div>
  );
}, (prevProps, nextProps) => {
  // Custom comparison: only re-render if item changed
  return prevProps.item.id === nextProps.item.id &&
         prevProps.item.name === nextProps.item.name &&
         prevProps.item.price === nextProps.item.price;
});

// ✅ useCallback for stable function reference
function ProductList() {
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [items, setItems] = useState(products);

  // Memoize callback to prevent ListItem re-renders
  const handleSelect = useCallback((id: string) => {
    setSelectedId(id);
  }, []);

  return (
    <div>
      {items.map(item => (
        <ListItem key={item.id} item={item} onSelect={handleSelect} />
      ))}
    </div>
  );
}

// ✅ useMemo for expensive calculations
function DataTable({ data }: { data: DataPoint[] }) {
  // Only recalculate when data changes
  const stats = useMemo(() => {
    console.log('Calculating stats...');
    return {
      total: data.reduce((sum, d) => sum + d.value, 0),
      average: data.reduce((sum, d) => sum + d.value, 0) / data.length,
      max: Math.max(...data.map(d => d.value)),
      min: Math.min(...data.map(d => d.value)),
    };
  }, [data]);

  return (
    <div>
      <div>Total: {stats.total}</div>
      <div>Average: {stats.average}</div>
      <div>Max: {stats.max}</div>
      <div>Min: {stats.min}</div>
    </div>
  );
}

// ✅ Context splitting to reduce re-renders
// Bad: Single context causes all consumers to re-render
const AppContext = createContext({
  user: null,
  theme: 'light',
  settings: {},
  notifications: [],
});

// Good: Split into separate contexts
const UserContext = createContext(null);
const ThemeContext = createContext('light');
const SettingsContext = createContext({});
const NotificationsContext = createContext([]);

// Components only re-render when their specific context changes
function UserProfile() {
  const user = useContext(UserContext); // Only re-renders on user change
  return <div>{user?.name}</div>;
}

function ThemeSwitcher() {
  const theme = useContext(ThemeContext); // Only re-renders on theme change
  return <button>{theme}</button>;
}

Example: Advanced re-render debugging

// Custom hook to track re-renders
function useRenderCount(componentName: string) {
  const renderCount = useRef(0);
  
  useEffect(() => {
    renderCount.current += 1;
    console.log(`[${componentName}] Render count: ${renderCount.current}`);
  });
  
  return renderCount.current;
}

// Hook to track prop changes
function useWhyDidYouUpdate(name: string, props: Record<string, any>) {
  const previousProps = useRef<Record<string, any>>();

  useEffect(() => {
    if (previousProps.current) {
      const allKeys = Object.keys({ ...previousProps.current, ...props });
      const changedProps: Record<string, any> = {};

      allKeys.forEach(key => {
        if (previousProps.current![key] !== props[key]) {
          changedProps[key] = {
            from: previousProps.current![key],
            to: props[key],
          };
        }
      });

      if (Object.keys(changedProps).length > 0) {
        console.log('[why-did-you-update]', name, changedProps);
      }
    }

    previousProps.current = props;
  });
}

// Usage in component
function ExpensiveComponent({ data, config, onUpdate }: Props) {
  const renderCount = useRenderCount('ExpensiveComponent');
  useWhyDidYouUpdate('ExpensiveComponent', { data, config, onUpdate });

  return <div>Rendered {renderCount} times</div>;
}

// Component splitting for optimization
// ❌ Bad: Entire form re-renders on any input change
function BadForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });

  return (
    <div>
      <input 
        value={formData.name}
        onChange={e => setFormData({...formData, name: e.target.value})}
      />
      <input 
        value={formData.email}
        onChange={e => setFormData({...formData, email: e.target.value})}
      />
      <ExpensivePreview data={formData} />
    </div>
  );
}

// ✅ Good: Split into controlled components
const FormInput = memo(({ value, onChange, name }: any) => {
  console.log(`Rendering input: ${name}`);
  return <input value={value} onChange={onChange} />;
});

function GoodForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const formData = useMemo(() => ({ name, email }), [name, email]);

  return (
    <div>
      <FormInput name="name" value={name} onChange={e => setName(e.target.value)} />
      <FormInput name="email" value={email} onChange={e => setEmail(e.target.value)} />
      <ExpensivePreview data={formData} />
    </div>
  );
}

5. Core Web Vitals and React Performance

Metric Target React Impact Optimization Strategy
LCP (Largest Contentful Paint) < 2.5s Initial render performance Code splitting, SSR, image optimization
FID (First Input Delay) < 100ms JavaScript execution time Reduce bundle size, defer non-critical JS
CLS (Cumulative Layout Shift) < 0.1 Dynamic content insertion Reserve space, avoid layout shifts
FCP (First Contentful Paint) < 1.8s Time to first render Minimize initial bundle, critical CSS
TTFB (Time to First Byte) < 600ms Server response time SSR optimization, CDN, caching
TBT (Total Blocking Time) < 200ms Long tasks blocking main thread Code splitting, Web Workers

Example: Core Web Vitals optimization

// LCP optimization - prioritize hero image
function HeroSection() {
  return (
    <section>
      <img
        src="/hero-large.jpg"
        alt="Hero"
        // Prioritize loading
        loading="eager"
        fetchpriority="high"
        // Prevent layout shift
        width={1200}
        height={600}
      />
    </section>
  );
}

// FID optimization - defer non-critical code
function App() {
  const [analyticsLoaded, setAnalyticsLoaded] = useState(false);

  useEffect(() => {
    // Load analytics after initial render
    requestIdleCallback(() => {
      import('./analytics').then(module => {
        module.initAnalytics();
        setAnalyticsLoaded(true);
      });
    });
  }, []);

  return <MainApp />;
}

// CLS optimization - prevent layout shifts
function ImageWithPlaceholder({ src, alt }: { src: string; alt: string }) {
  const [loaded, setLoaded] = useState(false);

  return (
    <div style={{ position: 'relative', width: 300, height: 200 }}>
      {/* Placeholder reserves space */}
      {!loaded && (
        <div 
          style={{ 
            position: 'absolute', 
            inset: 0, 
            backgroundColor: '#f0f0f0' 
          }} 
        />
      )}
      <img
        src={src}
        alt={alt}
        width={300}
        height={200}
        onLoad={() => setLoaded(true)}
        style={{ display: loaded ? 'block' : 'none' }}
      />
    </div>
  );
}

// Measure Core Web Vitals
import { onCLS, onFID, onLCP, onFCP, onTTFB } from 'web-vitals';

function reportWebVitals() {
  const sendToAnalytics = ({ name, value, id }: any) => {
    // Send to analytics endpoint
    fetch('/api/analytics', {
      method: 'POST',
      body: JSON.stringify({
        metric: name,
        value: Math.round(name === 'CLS' ? value * 1000 : value),
        id,
        timestamp: Date.now(),
      }),
      keepalive: true,
    });
  };

  onCLS(sendToAnalytics);
  onFID(sendToAnalytics);
  onLCP(sendToAnalytics);
  onFCP(sendToAnalytics);
  onTTFB(sendToAnalytics);
}

// Initialize in app
useEffect(() => {
  reportWebVitals();
}, []);

Example: React performance best practices

// Optimize initial load with React.lazy
const HeavyChart = lazy(() =>
  import(/* webpackChunkName: "chart" */ './HeavyChart')
);

function Dashboard() {
  return (
    <div>
      <Header /> {/* Render immediately */}
      <Suspense fallback={<ChartSkeleton />}>
        <HeavyChart /> {/* Load after initial render */}
      </Suspense>
    </div>
  );
}

// Prevent CLS with skeleton screens
function SkeletonCard() {
  return (
    <div 
      style={{ 
        width: '300px', 
        height: '200px', 
        backgroundColor: '#f0f0f0',
        borderRadius: '8px',
        animation: 'pulse 1.5s infinite',
      }}
    />
  );
}

function ProductCard({ id }: { id: string }) {
  const { data, loading } = useFetch<Product>(`/api/products/${id}`);

  if (loading) return <SkeletonCard />; // Same dimensions as actual card

  return (
    <div style={{ width: '300px', height: '200px' }}>
      <img src={data.image} alt={data.name} />
      <h3>{data.name}</h3>
    </div>
  );
}

// Optimize TBT with progressive hydration
function ProgressiveApp() {
  const [hydrated, setHydrated] = useState({
    header: false,
    content: false,
    footer: false,
  });

  useEffect(() => {
    // Hydrate header first
    setHydrated(prev => ({ ...prev, header: true }));
    
    requestIdleCallback(() => {
      // Hydrate content when idle
      setHydrated(prev => ({ ...prev, content: true }));
      
      requestIdleCallback(() => {
        // Hydrate footer last
        setHydrated(prev => ({ ...prev, footer: true }));
      });
    });
  }, []);

  return (
    <div>
      {hydrated.header ? <Header /> : <HeaderSSR />}
      {hydrated.content ? <Content /> : <ContentSSR />}
      {hydrated.footer ? <Footer /> : <FooterSSR />}
    </div>
  );
}

6. Production Performance Monitoring Tools

Tool Features Use Case Integration
Sentry Error tracking, performance monitoring, tracing Production error tracking, performance issues React SDK, automatic error boundaries
New Relic APM, browser monitoring, distributed tracing Full-stack performance monitoring Browser agent, React instrumentation
Datadog RUM Real user monitoring, session replay, metrics User experience analytics Browser SDK, React integration
LogRocket Session replay, performance monitoring, logs Debug production issues with replays React SDK, Redux integration
Lighthouse CI Automated performance testing, CI/CD integration Performance regression prevention GitHub Actions, GitLab CI
Web Vitals Library Measure Core Web Vitals in production Track real user metrics NPM package, analytics integration

Example: Production monitoring setup

// Sentry integration
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';

Sentry.init({
  dsn: 'YOUR_SENTRY_DSN',
  integrations: [
    new BrowserTracing(),
    new Sentry.Replay({
      maskAllText: true,
      blockAllMedia: true,
    }),
  ],
  
  // Performance monitoring
  tracesSampleRate: 0.1, // 10% of transactions
  
  // Session replay
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  
  // Environment
  environment: process.env.NODE_ENV,
  
  // Release tracking
  release: process.env.REACT_APP_VERSION,
  
  // Custom tags
  beforeSend(event, hint) {
    // Add custom context
    event.tags = {
      ...event.tags,
      'user.role': getCurrentUserRole(),
    };
    return event;
  },
});

// Wrap app with Sentry error boundary
function App() {
  return (
    <Sentry.ErrorBoundary fallback={ErrorFallback} showDialog>
      <Router />
    </Sentry.ErrorBoundary>
  );
}

// Track custom performance metrics
function trackCustomMetric(name: string, value: number) {
  Sentry.metrics.distribution(name, value, {
    unit: 'millisecond',
    tags: { page: window.location.pathname },
  });
}

// Component performance tracking
function ExpensiveComponent() {
  useEffect(() => {
    const startTime = performance.now();
    
    return () => {
      const duration = performance.now() - startTime;
      trackCustomMetric('ExpensiveComponent.renderTime', duration);
    };
  });
  
  return <div>...</div>;
}

Example: Multi-tool monitoring setup

// LogRocket setup
import LogRocket from 'logrocket';
import setupLogRocketReact from 'logrocket-react';

LogRocket.init('YOUR_APP_ID', {
  release: process.env.REACT_APP_VERSION,
  network: {
    requestSanitizer: request => {
      // Sanitize sensitive data
      if (request.headers['Authorization']) {
        request.headers['Authorization'] = '[REDACTED]';
      }
      return request;
    },
  },
});

setupLogRocketReact(LogRocket);

// Connect to Sentry
LogRocket.getSessionURL(sessionURL => {
  Sentry.configureScope(scope => {
    scope.setExtra('sessionURL', sessionURL);
  });
});

// New Relic Browser Agent
// Add to index.html or initialize programmatically
<script src="https://js-agent.newrelic.com/nr-loader.js"></script>
<script>
  window.NREUM.init = {
    applicationID: 'YOUR_APP_ID',
    licenseKey: 'YOUR_LICENSE_KEY',
    // Additional config
  };
</script>

// Datadog RUM
import { datadogRum } from '@datadog/browser-rum';

datadogRum.init({
  applicationId: 'YOUR_APP_ID',
  clientToken: 'YOUR_CLIENT_TOKEN',
  site: 'datadoghq.com',
  service: 'react-app',
  env: process.env.NODE_ENV,
  version: process.env.REACT_APP_VERSION,
  sessionSampleRate: 100,
  sessionReplaySampleRate: 20,
  trackUserInteractions: true,
  trackResources: true,
  trackLongTasks: true,
  defaultPrivacyLevel: 'mask-user-input',
});

// Start session replay
datadogRum.startSessionReplayRecording();

// Custom action tracking
function trackUserAction(name: string, context?: Record<string, any>) {
  datadogRum.addAction(name, context);
  LogRocket.track(name, context);
  Sentry.addBreadcrumb({
    message: name,
    data: context,
  });
}

// Web Vitals to all platforms
import { onCLS, onFID, onLCP } from 'web-vitals';

function sendToAllPlatforms(metric: any) {
  // Send to Sentry
  Sentry.metrics.distribution(metric.name, metric.value);
  
  // Send to Datadog
  datadogRum.addTiming(metric.name, metric.value);
  
  // Send to custom analytics
  fetch('/api/vitals', {
    method: 'POST',
    body: JSON.stringify(metric),
    keepalive: true,
  });
}

onCLS(sendToAllPlatforms);
onFID(sendToAllPlatforms);
onLCP(sendToAllPlatforms);

// Lighthouse CI configuration
// lighthouserc.js
module.exports = {
  ci: {
    collect: {
      startServerCommand: 'npm run serve',
      url: ['http://localhost:3000'],
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],
        'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};
Performance Monitoring Best Practices: Use React Profiler API for component-level insights, implement code splitting at route and component levels, detect and prevent memory leaks with proper cleanup, optimize re-renders with memo/useMemo/useCallback, monitor Core Web Vitals in production, integrate multiple monitoring tools for comprehensive coverage, set up automated performance testing in CI/CD, track custom business metrics, sanitize sensitive data in monitoring tools, establish performance budgets.

React Performance Monitoring and Optimization Summary