Docs For AI
Performance

Performance Monitoring

Metrics collection, performance budgets, and monitoring tools

Performance Monitoring

Monitoring is essential for maintaining performance over time. This covers metrics collection, tools, and establishing performance budgets.

Core Web Vitals Measurement

Using web-vitals Library

import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';

function sendToAnalytics(metric: Metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    delta: metric.delta,
    id: metric.id,
    navigationType: metric.navigationType,
  });

  // Use sendBeacon for reliability
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/analytics', body);
  } else {
    fetch('/analytics', { body, method: 'POST', keepalive: true });
  }
}

// Measure all Core Web Vitals
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

Performance Observer API

// Long Tasks (blocking main thread)
const longTaskObserver = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Long task detected:', {
      duration: entry.duration,
      startTime: entry.startTime,
      name: entry.name,
    });
  }
});
longTaskObserver.observe({ entryTypes: ['longtask'] });

// Resource Timing
const resourceObserver = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.entryType === 'resource') {
      console.log('Resource loaded:', {
        name: entry.name,
        duration: entry.duration,
        transferSize: (entry as PerformanceResourceTiming).transferSize,
      });
    }
  }
});
resourceObserver.observe({ entryTypes: ['resource'] });

// Navigation Timing
const navObserver = new PerformanceObserver((list) => {
  const entry = list.getEntries()[0] as PerformanceNavigationTiming;
  console.log('Navigation timing:', {
    domContentLoaded: entry.domContentLoadedEventEnd - entry.startTime,
    loadComplete: entry.loadEventEnd - entry.startTime,
    ttfb: entry.responseStart - entry.requestStart,
  });
});
navObserver.observe({ entryTypes: ['navigation'] });

Performance Budgets

Configuration

// budget.json
{
  "resourceSizes": [
    {
      "resourceType": "script",
      "budget": 300
    },
    {
      "resourceType": "stylesheet",
      "budget": 50
    },
    {
      "resourceType": "image",
      "budget": 500
    },
    {
      "resourceType": "total",
      "budget": 1000
    }
  ],
  "resourceCounts": [
    {
      "resourceType": "script",
      "budget": 15
    },
    {
      "resourceType": "third-party",
      "budget": 10
    }
  ],
  "timings": [
    {
      "metric": "first-contentful-paint",
      "budget": 1500
    },
    {
      "metric": "largest-contentful-paint",
      "budget": 2500
    },
    {
      "metric": "interactive",
      "budget": 3500
    },
    {
      "metric": "total-blocking-time",
      "budget": 300
    }
  ]
}

Lighthouse CI

# lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:3000/', 'http://localhost:3000/about'],
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],
        'first-contentful-paint': ['error', { maxNumericValue: 1500 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        'total-blocking-time': ['error', { maxNumericValue: 300 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};

CI Integration

# .github/workflows/lighthouse.yml
name: Lighthouse CI

on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install and Build
        run: |
          npm ci
          npm run build

      - name: Run Lighthouse CI
        uses: treosh/lighthouse-ci-action@v10
        with:
          configPath: ./lighthouserc.js
          uploadArtifacts: true
          temporaryPublicStorage: true

Real User Monitoring (RUM)

Custom RUM Implementation

class PerformanceMonitor {
  private metrics: Record<string, number> = {};
  private marks: Record<string, number> = {};

  mark(name: string) {
    this.marks[name] = performance.now();
    performance.mark(name);
  }

  measure(name: string, startMark: string, endMark?: string) {
    const start = this.marks[startMark];
    const end = endMark ? this.marks[endMark] : performance.now();

    if (start !== undefined) {
      this.metrics[name] = end - start;
      performance.measure(name, startMark, endMark);
    }
  }

  trackInteraction(name: string, fn: () => void | Promise<void>) {
    const start = performance.now();

    const result = fn();

    if (result instanceof Promise) {
      result.finally(() => {
        this.metrics[`interaction:${name}`] = performance.now() - start;
        this.report();
      });
    } else {
      this.metrics[`interaction:${name}`] = performance.now() - start;
      this.report();
    }
  }

  private report() {
    if (Object.keys(this.metrics).length > 0) {
      navigator.sendBeacon('/api/metrics', JSON.stringify({
        metrics: this.metrics,
        url: window.location.href,
        timestamp: Date.now(),
        userAgent: navigator.userAgent,
      }));
    }
  }
}

// Usage
const monitor = new PerformanceMonitor();

// Track component render
monitor.mark('component:start');
// ... render logic
monitor.measure('component:render', 'component:start');

// Track user interaction
monitor.trackInteraction('submit-form', async () => {
  await submitForm(data);
});

Third-Party RUM Services

// Sentry Performance
import * as Sentry from '@sentry/react';

Sentry.init({
  dsn: 'YOUR_DSN',
  integrations: [
    new Sentry.BrowserTracing({
      tracePropagationTargets: ['localhost', /^https:\/\/api\.example\.com/],
    }),
  ],
  tracesSampleRate: 0.1, // 10% of transactions
});

// Custom transaction
const transaction = Sentry.startTransaction({
  name: 'User Checkout',
  op: 'checkout',
});

// Add spans
const span = transaction.startChild({
  op: 'http',
  description: 'Fetch cart items',
});
await fetchCartItems();
span.finish();

transaction.finish();

Development Tools

Chrome DevTools Performance Panel

// Programmatic performance marks for DevTools
performance.mark('app:init:start');
initializeApp();
performance.mark('app:init:end');
performance.measure('App Initialization', 'app:init:start', 'app:init:end');

// Console timing
console.time('expensive-operation');
expensiveOperation();
console.timeEnd('expensive-operation');

// Memory profiling
console.memory; // Chrome only

React DevTools Profiler

import { Profiler, ProfilerOnRenderCallback } from 'react';

const onRenderCallback: ProfilerOnRenderCallback = (
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime
) => {
  console.log({
    id,
    phase,
    actualDuration,
    baseDuration,
    startTime,
    commitTime,
  });
};

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

Error and Performance Correlation

// Track errors with performance context
window.addEventListener('error', (event) => {
  const performanceData = {
    memory: (performance as any).memory,
    timing: performance.timing,
    longTasks: getLongTaskCount(),
  };

  reportError({
    message: event.message,
    filename: event.filename,
    lineno: event.lineno,
    performance: performanceData,
  });
});

// Track slow network requests
const originalFetch = window.fetch;
window.fetch = async (...args) => {
  const start = performance.now();
  const url = typeof args[0] === 'string' ? args[0] : args[0].url;

  try {
    const response = await originalFetch(...args);
    const duration = performance.now() - start;

    if (duration > 3000) {
      reportSlowRequest({ url, duration });
    }

    return response;
  } catch (error) {
    const duration = performance.now() - start;
    reportFailedRequest({ url, duration, error });
    throw error;
  }
};

Alerting and Dashboards

Performance Alerts

// Server-side alert configuration
const alertRules = {
  lcp: {
    threshold: 2500,
    percentile: 75,
    window: '1h',
    action: 'slack',
  },
  cls: {
    threshold: 0.1,
    percentile: 75,
    window: '1h',
    action: 'email',
  },
  errorRate: {
    threshold: 0.01, // 1%
    window: '15m',
    action: 'pagerduty',
  },
};

// Check and alert
async function checkAlerts(metrics: MetricData[]) {
  for (const [metric, rule] of Object.entries(alertRules)) {
    const value = calculatePercentile(
      metrics.filter(m => m.name === metric),
      rule.percentile
    );

    if (value > rule.threshold) {
      await sendAlert(rule.action, {
        metric,
        value,
        threshold: rule.threshold,
      });
    }
  }
}

Best Practices

Monitoring Guidelines

  1. Measure Core Web Vitals for all pages
  2. Set performance budgets and enforce in CI
  3. Use RUM to understand real user experience
  4. Correlate performance with business metrics
  5. Set up alerts for performance regressions
  6. Track performance trends over time
  7. Segment data by device, network, geography
  8. Review performance weekly with stakeholders

On this page