Skip to content
fearchitect
Quality, Observability & Cross-Cutting

Frontend Observability

Capture errors, measure real-user performance, and trace what breaks in production.

By Abas TurabliReviewed

Summary

Frontend observability spans three layers: JavaScript error capture (window.onerror, unhandledrejection, framework error boundaries), real-user monitoring of Core Web Vitals via the web-vitals library, and network/API error tracking. Tools like Sentry add distributed tracing and session replay. Source maps tie minified stack traces back to source code.

Jump to the interview angle

Frontend observability

The practice of collecting errors, performance metrics, and behavioral traces from real browsers so you can diagnose production failures without reproducing them locally. It splits into three complementary layers: unhandled JS errors, framework-level error boundaries, and network/API failures. Each catches a different failure class; all three together give full coverage.

Three error-capture layers

  • window.onerror + window.onunhandledrejection — catches synchronous throws and unhandled promise rejections that escape all other handlers.
  • Framework error boundaries (React ErrorBoundary, Vue errorHandler, Angular ErrorHandler) — catch render-phase exceptions and let you show a fallback UI.
  • Network/API errors — fetch wrappers or interceptors that track 4xx/5xx responses, timeouts, and offline failures that produce no JS exception.

Sentry init + web-vitals RUM in one module

Initialize Sentry once at app entry before any other imports, then wire web-vitals callbacks to send INP, LCP, and CLS to the same analytics endpoint. Both run in the browser only.

observability.ts — Sentry + web-vitals setupts
import * as Sentry from "@sentry/browser";
import { onINP, onLCP, onCLS } from "web-vitals";
import type { Metric } from "web-vitals";

// --- Error tracking & tracing ---
Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  // Attach traces to 20 % of sessions to stay within quota.
  tracesSampleRate: 0.2,
  // Session replay: 10 % of sessions, 100 % of sessions with an error.
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  integrations: [
    Sentry.replayIntegration({
      // Block any element whose selector or text matches these patterns.
      blockAllMedia: true,
      maskAllText: true, // redacts all text nodes by default
    }),
  ],
});

// --- Real-user monitoring ---
function sendVital(metric: Metric): void {
  // navigator.sendBeacon queues the POST even if the page is unloading.
  navigator.sendBeacon(
    "/api/vitals",
    JSON.stringify({
      name: metric.name,    // "INP" | "LCP" | "CLS"
      value: metric.value,  // ms for INP/LCP; unitless score for CLS
      rating: metric.rating, // "good" | "needs-improvement" | "poor"
      id: metric.id,
    }),
  );
}

onINP(sendVital, { reportAllChanges: true });
onLCP(sendVital);
onCLS(sendVital, { reportAllChanges: true });

maskAllText: true in replayIntegration redacts every text node before upload — default-safe for PII. Raise replaysOnErrorSampleRate to 1.0 only; keep session rate low to control storage cost.

Session replay and PII

Session replay records DOM snapshots. Without masking, it captures passwords, payment fields, and personal data. Sentry's maskAllText: true and blockAllMedia: true are safe defaults — opt individual elements out with data-sentry-unmask, not in. Verify your replay vendor's data-processing agreement covers your GDPR / CCPA obligations before enabling in production.

Trade-offs of a full observability stack

Pros

  • Source maps turn minified line numbers into actionable file + line references for every error.
  • Session replay cuts mean-time-to-reproduce from hours to minutes for hard-to-repro bugs.
  • RUM with web-vitals gives real-device INP/LCP data that Lighthouse cannot replicate.
  • Distributed tracing links a frontend error to the exact backend span, skipping log trawling.

Cons

  • Sentry SDK adds ~50 kB gzipped; replay integration adds another ~20 kB.
  • Source maps uploaded to a public CDN expose your original code — upload only to Sentry, not the CDN.
  • High replay sample rates can breach storage quotas and slow the SDK's background thread.
  • window.onerror misses cross-origin script errors unless the server sets Access-Control-Allow-Origin and the script tag has crossorigin.

Interview angle

Interviewers probe which error types each capture layer catches, why source maps are required for actionable stack traces, and how session replay intersects with GDPR.

Soundbite: "Three layers: global JS errors, framework error boundaries, and failed fetches — miss any one and you have a blind spot."

Key terms

window.onerror
Global browser handler for uncaught synchronous JS errors; receives message, source, line, col, and the Error object.
unhandledrejection
Window event fired when a Promise rejects with no .catch() handler attached before the microtask checkpoint.
Error boundary
React class component (or its equivalent in other frameworks) that catches render-phase exceptions and renders a fallback subtree.
RUM
Real-User Monitoring — collecting performance metrics (LCP, INP, CLS) and error data from actual user browsers in production.
Source map
A .map file that maps minified/transpiled output back to original source file, line, and column for readable stack traces.

Further reading

Search fearchitect

Jump to a topic, mode, or action.