Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/getsentry/sentry-javascript/llms.txt

Use this file to discover all available pages before exploring further.

Performance monitoring helps identify slow operations and optimize user experience while minimizing overhead on your application.

Enabling Performance Monitoring

Browser Applications

import * as Sentry from '@sentry/browser';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    Sentry.browserTracingIntegration(),
  ],
  tracesSampleRate: 0.1, // Capture 10% of transactions
});

Node.js Applications

import * as Sentry from '@sentry/node';

// MUST be first import
Sentry.init({
  dsn: '__DSN__',
  tracesSampleRate: 0.1,
});

// Now import other modules
import express from 'express';
For Node.js SDK v8+, you MUST initialize Sentry before any other imports for OpenTelemetry instrumentation to work.

Instrumentation

Automatic Instrumentation

The SDK automatically instruments: Browser:
  • Page load transactions
  • Navigation transactions
  • Fetch/XHR requests
  • User interactions (clicks, form inputs)
  • Web Vitals (LCP, FID, CLS, INP, TTFB)
Node.js:
  • HTTP/HTTPS requests
  • Express.js routes
  • Database queries (Prisma, MongoDB, MySQL, PostgreSQL)
  • GraphQL operations
  • Redis operations
// Browser
Sentry.init({
  dsn: '__DSN__',
  integrations: [
    Sentry.browserTracingIntegration({
      // Customize instrumentation
      traceFetch: true,
      traceXHR: true,
      enableLongAnimationFrame: true,
    }),
  ],
});

// Node.js - integrations are auto-enabled
Sentry.init({
  dsn: '__DSN__',
  tracesSampleRate: 0.1,
  // These are enabled by default:
  // - httpIntegration()
  // - expressIntegration()
  // - prismaIntegration()
  // - etc.
});

Manual Instrumentation

Create custom spans for specific operations:
import { startSpan } from '@sentry/browser';

const result = await startSpan(
  {
    name: 'complex-calculation',
    op: 'function',
    attributes: {
      'calc.type': 'fibonacci',
      'calc.input': n,
    },
  },
  async (span) => {
    const result = await fibonacci(n);
    span.setAttribute('calc.result', result);
    return result;
  }
);

Nested Spans

Track sub-operations:
await startSpan(
  { name: 'process-order', op: 'task' },
  async (parentSpan) => {
    await startSpan(
      { name: 'validate-order', op: 'validation' },
      async () => {
        await validateOrder(order);
      }
    );
    
    await startSpan(
      { name: 'charge-payment', op: 'payment' },
      async () => {
        await chargePayment(order);
      }
    );
    
    await startSpan(
      { name: 'create-shipment', op: 'logistics' },
      async () => {
        await createShipment(order);
      }
    );
  }
);

Controlling Span Creation

HTTP Instrumentation

Control which requests create spans:
import { httpIntegration } from '@sentry/node';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    httpIntegration({
      tracing: {
        // Don't create spans for certain URLs
        shouldCreateSpanForRequest: (url) => {
          return !url.includes('/health') && 
                 !url.includes('/metrics');
        },
      },
    }),
  ],
});

Browser Request Instrumentation

import { browserTracingIntegration } from '@sentry/browser';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    browserTracingIntegration({
      shouldCreateSpanForRequest: (url) => {
        // Don't trace analytics or ads
        return !url.match(/google-analytics|doubleclick/);
      },
    }),
  ],
});

Trace Propagation

Control distributed tracing headers:
Sentry.init({
  dsn: '__DSN__',
  // Propagate to same origin by default
  // Customize for specific targets:
  tracePropagationTargets: [
    'localhost',
    /^https:\/\/api\.myapp\.com/,
    /^\/api\//,  // Same-origin API routes
  ],
});
In v8+, trace propagation defaults to same-origin requests. Only set tracePropagationTargets for cross-origin distributed tracing.

Web Vitals

Monitor Core Web Vitals automatically:
Sentry.init({
  dsn: '__DSN__',
  integrations: [
    Sentry.browserTracingIntegration(),
  ],
});

// Captured automatically:
// - Largest Contentful Paint (LCP)
// - First Input Delay (FID) - deprecated, replaced by INP in v10+
// - Cumulative Layout Shift (CLS)
// - Interaction to Next Paint (INP)
// - Time to First Byte (TTFB)

Custom Measurements

Add custom performance measurements:
import { setMeasurement, startSpan } from '@sentry/browser';

startSpan({ name: 'page-load' }, () => {
  // Custom measurement
  setMeasurement('custom.metric', 123, 'millisecond');
  
  // Multiple measurements
  setMeasurement('bundle.size', 450, 'kilobyte');
  setMeasurement('api.calls', 5, 'none');
});

Transaction Naming

Browser Route Naming

import { browserTracingIntegration } from '@sentry/react';
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    browserTracingIntegration(),
  ],
});

// React Router integration handles naming automatically

Custom Transaction Names

import { getActiveSpan, getRootSpan } from '@sentry/browser';

// Update transaction name dynamically
const activeSpan = getActiveSpan();
if (activeSpan) {
  const rootSpan = getRootSpan(activeSpan);
  rootSpan.updateName('Custom Transaction Name');
}

Node.js Route Naming

import express from 'express';

const app = express();

app.get('/users/:id', (req, res) => {
  // Automatically named: GET /users/:id
  res.json({ id: req.params.id });
});

Sampling Strategies

Prioritize Important Transactions

Sentry.init({
  dsn: '__DSN__',
  tracesSampler: (samplingContext) => {
    // Always sample checkout flows
    if (samplingContext.name?.includes('/checkout')) {
      return 1.0;
    }
    
    // Sample API calls moderately
    if (samplingContext.name?.startsWith('/api/')) {
      return 0.2;
    }
    
    // Don't sample health checks
    if (samplingContext.name === '/health') {
      return 0;
    }
    
    // Default sampling
    return 0.05;
  },
});

Environment-Based Sampling

const isProd = process.env.NODE_ENV === 'production';

Sentry.init({
  dsn: '__DSN__',
  tracesSampleRate: isProd ? 0.05 : 1.0,
});

Performance Overhead Reduction

Disable Unnecessary Integrations

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    // Only enable what you need
    Sentry.browserTracingIntegration({
      // Disable interaction tracking if not needed
      traceFetch: true,
      traceXHR: true,
    }),
  ],
});

Limit Breadcrumb Collection

import { breadcrumbsIntegration } from '@sentry/browser';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    breadcrumbsIntegration({
      console: false, // Disable console breadcrumbs
      dom: true,
      fetch: true,
      history: true,
      sentry: false,
      xhr: true,
    }),
  ],
  maxBreadcrumbs: 50, // Default is 100
});

Selective Instrumentation

import { httpIntegration } from '@sentry/node';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    httpIntegration({
      // Disable if you don't need HTTP spans
      spans: false,
    }),
  ],
});

Framework-Specific Optimizations

React

import * as Sentry from '@sentry/react';
import {
  createRoutesFromChildren,
  matchRoutes,
  useLocation,
  useNavigationType,
} from 'react-router-dom';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    Sentry.reactRouterV6BrowserTracingIntegration({
      useEffect: React.useEffect,
      useLocation,
      useNavigationType,
      createRoutesFromChildren,
      matchRoutes,
    }),
  ],
  tracesSampleRate: 0.1,
});

Next.js

Next.js SDK handles instrumentation automatically:
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: '__DSN__',
  tracesSampleRate: 0.1,
  // Browser tracing is automatic
});

// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: '__DSN__',
  tracesSampleRate: 0.1,
  // Server instrumentation is automatic
});

Vue

import * as Sentry from '@sentry/vue';

Sentry.init({
  app,
  dsn: '__DSN__',
  integrations: [
    Sentry.browserTracingIntegration({
      router, // Pass Vue Router instance
    }),
  ],
  tracesSampleRate: 0.1,
});

Database Query Instrumentation

Prisma

import { prismaIntegration } from '@sentry/node';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    prismaIntegration(),
  ],
});

// Queries are automatically instrumented
const users = await prisma.user.findMany();

MongoDB (Mongoose)

import { mongooseIntegration } from '@sentry/node';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    mongooseIntegration(),
  ],
});

Best Practices

High-traffic applications should use lower sample rates:
const sampleRate = process.env.NODE_ENV === 'production' ? 0.05 : 1.0;

Sentry.init({
  dsn: '__DSN__',
  tracesSampleRate: sampleRate,
});
These create noise without value:
tracesSampler: (context) => {
  if (context.name?.match(/\/(health|metrics|ping)/)) {
    return 0;
  }
  return 0.1;
}
Set appropriate op values for better organization:
startSpan({ name: 'db-query', op: 'db.query' }, () => {});
startSpan({ name: 'api-call', op: 'http.client' }, () => {});
startSpan({ name: 'render', op: 'ui.render' }, () => {});
Too many spans can increase overhead. Focus on meaningful operations:
// ❌ Too granular
for (const item of items) {
  startSpan({ name: 'process-item' }, () => processItem(item));
}

// ✅ Better
startSpan({ name: 'process-items' }, () => {
  items.forEach(processItem);
});
Add context to help debug performance issues:
startSpan(
  {
    name: 'fetch-user-data',
    op: 'http.client',
    attributes: {
      'http.method': 'GET',
      'http.url': '/api/users/123',
      'user.id': '123',
    },
  },
  () => {}
);

Monitoring Performance Impact

Check SDK Overhead

const start = performance.now();

// Your operation
await myOperation();

const duration = performance.now() - start;
console.log('Operation duration:', duration);

Profile Mode

Enable profiling for deeper insights:
import { browserProfilingIntegration } from '@sentry/browser';

Sentry.init({
  dsn: '__DSN__',
  integrations: [
    browserProfilingIntegration(),
  ],
  tracesSampleRate: 0.1,
  profilesSampleRate: 0.1, // Relative to traces
});

Complete Example

import * as Sentry from '@sentry/browser';
import { startSpan } from '@sentry/browser';

const environment = process.env.NODE_ENV;
const isProd = environment === 'production';

Sentry.init({
  dsn: '__DSN__',
  environment,
  
  integrations: [
    Sentry.browserTracingIntegration({
      // Customize what's traced
      traceFetch: true,
      traceXHR: true,
      enableLongAnimationFrame: true,
      
      // Filter requests
      shouldCreateSpanForRequest: (url) => {
        return !url.match(/(\.png|\.jpg|analytics|ads)/);
      },
    }),
  ],
  
  // Sampling strategy
  tracesSampler: (context) => {
    // Never sample health checks
    if (context.name === '/health') return 0;
    
    // Always sample checkout
    if (context.name?.includes('/checkout')) return 1.0;
    
    // Production: low rate
    // Development: high rate
    return isProd ? 0.05 : 1.0;
  },
  
  // Propagate to your API
  tracePropagationTargets: [
    'localhost',
    /^https:\/\/api\.myapp\.com/,
  ],
  
  // Reduce breadcrumbs
  maxBreadcrumbs: 50,
});

// Manual instrumentation example
export async function complexOperation(userId) {
  return await startSpan(
    {
      name: 'complex-operation',
      op: 'task',
      attributes: { 'user.id': userId },
    },
    async (span) => {
      // Step 1
      await startSpan(
        { name: 'fetch-user', op: 'http.client' },
        async () => {
          const user = await fetchUser(userId);
          span.setAttribute('user.role', user.role);
          return user;
        }
      );
      
      // Step 2
      await startSpan(
        { name: 'process-data', op: 'function' },
        async () => {
          return await processUserData(user);
        }
      );
    }
  );
}

Next Steps

Bundle Size Optimization

Reduce SDK bundle size impact

Source Maps

Configure source maps for production

Build docs developers (and LLMs) love