Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Integrate real-time error monitoring in production

A real-time error monitoring service captures, groups, and alerts on unhandled exceptions and promise rejections in production so issues are discovered before users report them.

Utilities
Quick take
Typical fix time 30 min
  • Install an error monitoring SDK (Sentry, Datadog, Bugsnag, etc.) early in the app entry point
  • Capture unhandled exceptions, unhandled promise rejections, and React error boundaries
  • Attach user context, release version, and environment to every event
  • Configure alert thresholds and on-call routing so critical errors page the right team
  • Capture failed network requests and production RUM signals such as Core Web Vitals
Why it matters: Production errors are invisible without monitoring. Users rarely file detailed bug reports — they simply leave. Error monitoring gives you actionable stack traces, breadcrumbs, and user context within seconds of an issue occurring, cutting mean time to detect (MTTD) from days to minutes and dramatically reducing the cost of incidents.

Rule Details

Error monitoring closes the feedback loop between your production environment and your engineering team. Without it, you are flying blind — learning about bugs only when users complain.

Code Examples

Installation

pnpm add @sentry/nextjs
# or for plain React:
pnpm add @sentry/react

Next.js (App Router)

// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
 
Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
 
  // Only enable in production — avoids noise during development
  enabled: process.env.NODE_ENV === 'production',
 
  // Tag every event with the deployed version for release tracking
  release: process.env.NEXT_PUBLIC_APP_VERSION,
  environment: process.env.NEXT_PUBLIC_ENVIRONMENT ?? 'production',
 
  // Sample rate: 1.0 = capture 100% of errors (recommended to start)
  sampleRate: 1.0,
 
  // Performance monitoring — sample 10% of transactions
  tracesSampleRate: 0.1,
 
  // Session replays — capture 10% of sessions, 100% of errored sessions
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
 
  integrations: [
    Sentry.replayIntegration({
      // Mask all text and inputs to comply with privacy requirements
      maskAllText: true,
      blockAllMedia: true,
    }),
  ],
 
  beforeSend(event, hint) {
    // Strip PII from error events before they leave the browser
    if (event.user?.email) {
      event.user.email = '[Filtered]'
    }
    return event
  },
})
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'
 
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  enabled: process.env.NODE_ENV === 'production',
  release: process.env.APP_VERSION,
  environment: process.env.ENVIRONMENT ?? 'production',
  sampleRate: 1.0,
  tracesSampleRate: 0.05,
})

Identifying Users

Attach user context after authentication so errors can be traced to specific accounts (with appropriate privacy considerations):

// After successful login
import * as Sentry from '@sentry/nextjs'
 
function onUserLogin(user: { id: string; plan: string }) {
  Sentry.setUser({
    id: user.id,          // Use an opaque ID, never email/name
    plan: user.plan,      // Non-PII attributes are useful for triage
  })
}
 
function onUserLogout() {
  Sentry.setUser(null)
}

React Error Boundary

// components/error-boundary.tsx
import { Component, type ErrorInfo, type ReactNode } from 'react'
import * as Sentry from '@sentry/react'
 
interface Props {
  children: ReactNode
  fallback?: ReactNode
}
 
interface State {
  hasError: boolean
  eventId: string | null
}
 
export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false, eventId: null }
 
  static getDerivedStateFromError(): Partial<State> {
    return { hasError: true }
  }
 
  componentDidCatch(error: Error, info: ErrorInfo) {
    const eventId = Sentry.captureException(error, {
      contexts: { react: { componentStack: info.componentStack ?? '' } },
    })
    this.setState({ eventId })
  }
 
  render() {
    if (this.state.hasError) {
      return (
        this.props.fallback ?? (
          <div role="alert">
            <h2>Something went wrong.</h2>
            <button
              onClick={() =>
                Sentry.showReportDialog({ eventId: this.state.eventId ?? '' })
              }
            >
              Report feedback
            </button>
          </div>
        )
      )
    }
    return this.props.children
  }
}

Capturing Custom Errors

import * as Sentry from '@sentry/nextjs'
 
// Capture a specific error with extra context
async function processPayment(orderId: string, amount: number) {
  try {
    await paymentGateway.charge({ orderId, amount })
  } catch (error) {
    Sentry.withScope((scope) => {
      scope.setTag('feature', 'checkout')
      scope.setContext('order', { orderId, amount })
      scope.setLevel('error')
      Sentry.captureException(error)
    })
    throw error  // Re-throw — never swallow errors silently
  }
}
 
// Log a message (non-exception) for important events
Sentry.captureMessage('Payment gateway returned unexpected status code 202', 'warning')

Why It Matters

Production errors are invisible without monitoring. Users rarely file detailed bug reports — they simply leave. Error monitoring gives you actionable stack traces, breadcrumbs, and user context within seconds of an issue occurring, cutting mean time to detect (MTTD) from days to minutes and dramatically reducing the cost of incidents.

What to Monitor

SignalDescription
Unhandled exceptionsJavaScript errors not caught by any try-catch
Unhandled promise rejectionsAsync errors that bubble to the global handler
Framework error boundariesReact, Vue, Angular component-level crashes
Network errorsFailed API calls and HTTP 4xx/5xx responses
Custom errorsBusiness logic violations you explicitly capture
Performance anomaliesCore Web Vitals regressions, slow transactions

Production RUM and Failed Requests

User-facing regressions often show up first as slow interactions or a burst of failed API calls rather than a top-level crash. Capture both:

import { onCLS, onINP, onLCP } from 'web-vitals'
 
function reportWebVital(name: string, value: number) {
  monitoring.captureMessage(`web-vital:${name}`, 'info', {
    tags: { source: 'rum' },
    extra: { value },
  })
}
 
onLCP((metric) => reportWebVital('LCP', metric.value))
onCLS((metric) => reportWebVital('CLS', metric.value))
onINP((metric) => reportWebVital('INP', metric.value))
 
async function monitoredFetch(input: RequestInfo | URL, init?: RequestInit) {
  const response = await fetch(input, init)
  if (!response.ok) {
    monitoring.captureMessage('frontend-network-failure', 'warning', {
      tags: { status: String(response.status) },
      extra: { url: String(input) },
    })
  }
  return response
}

Alternative: Global Error Handlers

For applications that do not use a monitoring SDK, at minimum capture global errors:

// Unhandled exceptions
window.addEventListener('error', (event) => {
  sendToMonitoring({
    type: 'exception',
    message: event.message,
    source: event.filename,
    line: event.lineno,
    column: event.colno,
    stack: event.error?.stack,
  })
})
 
// Unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
  sendToMonitoring({
    type: 'unhandledRejection',
    reason: String(event.reason),
    stack: event.reason instanceof Error ? event.reason.stack : undefined,
  })
})

Alert Configuration

Configure alerts that page the right people without creating noise:

RuleThresholdChannel
New error (never seen before)ImmediatelySlack #engineering
Error rate spike+200% vs previous hourPagerDuty (on-call)
Critical unhandled exceptionAny occurrence in prodSlack + email
Issue regression (fixed → reoccurring)ImmediatelySlack + assignee
LCP/INP regressionSustained breach vs baselinePerformance alert channel
Failed request spikeAbove normal baseline per endpointOn-call or API owners
Do not monitor in development or test environments

Initialising the SDK in development produces noise and can exhaust your event quota. Always guard with process.env.NODE_ENV === 'production' or use separate DSNs per environment so staging errors do not page the on-call engineer.

Standards

  • Use these references as the standard for how the test or monitoring strategy should behave in the shipped workflow.
  • Check the implementation against Sentry: Getting started with JavaScript before treating the rule as satisfied.
  • Check the implementation against web.dev: Monitor and analyze the app before treating the rule as satisfied.

Verification

  1. In a staging environment, trigger a deliberate throw new Error('Test monitoring') and confirm the event appears in your monitoring dashboard within 30 seconds.
  2. Check that the event includes the correct release tag, environment, and user context.
  3. Confirm a CI deployment hook uploads source maps so stack traces show original TypeScript line numbers, not minified output.
  4. Verify that at least one alert rule exists for new production errors and is routed to the on-call channel.
  5. Force a failed API response and confirm the monitoring system records the request failure with route and status.
  6. Confirm production RUM captures LCP, CLS, or INP and that alerting is based on regression thresholds, not just raw logs.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Check whether an error monitoring service is configured and capturing unhandled errors, promise rejections, React/framework-level errors, failed network requests, and production Core Web Vitals signals.

Fix

Auto-fix issues

Integrate an error monitoring SDK, configure environment and release tagging, capture failed network requests and RUM metrics, and set up alerts for critical error thresholds and regressions.

Explain

Learn more

Explain how error monitoring services work, what signals they capture, and how error grouping reduces alert noise.

Review

Code review

Review the monitoring initialisation code. Flag missing user context, absent release tagging, overly broad event filters, missing network instrumentation, and any patterns that would swallow errors before they reach the monitoring service.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Sentry
sentry.ioTool

Rules that often go hand-in-hand with this one.

Use mutation testing to measure how well tests detect bugs

Run Stryker mutation testing on critical business logic to verify that your test suite will actually catch real bugs, not just achieve line coverage.

Testing
Remove console statements in production

Remove or disable console.log, console.debug, and other console statements before deploying to production.

JavaScript
Maintain test coverage thresholds

Set and enforce minimum code coverage thresholds to ensure adequate test coverage.

Testing
Implement proper error handling

Use try-catch blocks and error boundaries to gracefully handle errors in async operations and UI components.

JavaScript

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385