Install @opentelemetry/sdk-trace-web, @opentelemetry/instrumentation-fetch, @opentelemetry/instrumentation-document-load, and a suitable exporter (e.g., @opentelemetry/exporter-trace-otlp-http); initialize a WebTracerProvider early in the application entry point.
Register the DocumentLoadInstrumentation to automatically create a root span for the initial page load that includes sub-spans for DNS lookup, TCP connection, request, and response phases derived from the Navigation Timing API.
Register the FetchInstrumentation (and optionally XMLHttpRequestInstrumentation) with propagateTraceHeaderCorsUrls set to a list of your backend origins; this injects W3C TraceContext headers into outbound requests and links frontend and backend spans.
Collect Core Web Vitals (LCP, INP, CLS) using the web-vitals npm library; create manual OTel spans or record them as histogram measurements on an OTel Meter, attaching the current trace context so CWV data links to the active page-load span.
Configure the OTLP HTTP exporter to send spans to an OTel Collector or a compatible backend; set the exporter's url to the collector's OTLP/HTTP endpoint, and configure CORS headers on the collector to allow browser-origin requests.
Verify spans appear in your backend by searching for the service.name resource attribute set in your WebTracerProvider resource; use Jaeger, Grafana Tempo, or another trace UI to confirm parent-child span relationships between browser and server spans.
Known gotchas
Browser CORS restrictions block OTLP exports unless the OTel Collector or backend explicitly allows the page's origin via CORS response headers; missing Access-Control-Allow-Origin causes all spans to be silently dropped with a network error in the browser console.
The web-vitals library measures INP using PerformanceEventTiming, which is only available in Chromium-based browsers; Firefox and Safari do not support INP measurement natively, so INP data will be absent for those users.
OTel JS SDK context propagation in SPAs relies on the async context manager (ZoneContextManager or AsyncLocalStorageContextManager); if spans are created outside the managed async context (e.g., in a setTimeout callback with no active context), they become root spans with no parent, fragmenting traces.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp