Skip to content

Core Web Vitals & Real-User Performance Monitoring

Status: Active Standard (Floorix Implemented; DotPortal, Govnix, Perfxcel in Rollout)
Telemetry Pipeline: Dual Reporting (Sentry Transactions + Umami Custom Events)
Target Library: web-vitals v6
Last Updated: October 2026


1. Overview & Objectives

In manufacturing and governance SaaS applications, real-world operators frequently use low-spec shop-floor terminals, tablets, and variable cellular connections. To prevent performance degradation from silently impacting operations, DotEvolve applications measure real-user Web Vitals at runtime.

This standard implements Google's Core Web Vitals (CWV) metrics across all DotEvolve frontend applications: 1. LCP (Largest Contentful Paint): Perceived load speed of the primary UI element. 2. INP (Interaction to Next Paint): Responsiveness to user input (clicks, taps, keystrokes). 3. CLS (Cumulative Layout Shift): Visual stability during rendering. 4. FCP (First Contentful Paint): Initial visual feedback after page request. 5. TTFB (Time to First Byte): Network and server response latency.


2. Core Web Vitals Targets

All DotEvolve client applications aim for the 75th percentile of real user visits:

Metric Full Name Good (Target) Needs Improvement Poor
LCP Largest Contentful Paint $\le 2.5\text{ s}$ $2.5\text{ s} - 4.0\text{ s}$ $> 4.0\text{ s}$
INP Interaction to Next Paint $\le 200\text{ ms}$ $200\text{ ms} - 500\text{ ms}$ $> 500\text{ ms}$
CLS Cumulative Layout Shift $\le 0.1$ $0.1 - 0.25$ $> 0.25$
FCP First Contentful Paint $\le 1.8\text{ s}$ $1.8\text{ s} - 3.0\text{ s}$ $> 3.0\text{ s}$
TTFB Time to First Byte $\le 800\text{ ms}$ $800\text{ ms} - 1800\text{ ms}$ $> 1800\text{ ms}$

3. Architecture & Telemetry Pipeline

Web Vitals metrics are measured client-side via the modern Performance Observer API (encapsulated in web-vitals) and reported simultaneously to Sentry and Umami:

flowchart LR
    subgraph Browser ["Client Browser (SPA)"]
        WebVitals["web-vitals v6<br/>(onLCP, onINP, onCLS, onFCP, onTTFB)"]
        Init["initWebVitals()"]
    end

    subgraph Telemetry ["Telemetry Pipeline"]
        Sentry["Sentry Error & Performance<br/>(Breadcrumbs & Measurements)"]
        Umami["Self-Hosted Umami v2<br/>(Custom Events: 'web-vital')"]
    end

    subgraph Observability ["Engineering Dashboards"]
        SentryDash["Sentry Performance Insights"]
        UmamiDash["Umami Platform Telemetry"]
    end

    WebVitals --> Init
    Init -->|captureBreadcrumb / setMeasurement| Sentry
    Init -->|umami.track('web-vital')| Umami
    Sentry --> SentryDash
    Umami --> UmamiDash

Dual-Dispatch Advantages

  1. Sentry (Diagnostic Debugging): Correlates high INP or slow LCP directly with JavaScript errors and slow API transaction spans.
  2. Umami (Platform Aggregate Telemetry): Provides privacy-friendly, un-sampled aggregate percentiles per tenant and geographic region without sending PII.

4. Implementation Reference

4.1 Implementation Code (src/utils/vitals.ts)

import { onCLS, onFCP, onINP, onLCP, onTTFB, type Metric } from 'web-vitals';
import { Sentry } from '@dotevolve/error-utils/react';

declare global {
  interface Window {
    umami?: {
      track: (eventName: string, eventData?: Record<string, any>) => void;
    };
  }
}

export function reportWebVitals(metric: Metric): void {
  const { name, value, rating, delta, id } = metric;

  // 1. Send diagnostic measurement to Sentry
  try {
    Sentry.addBreadcrumb({
      category: 'web-vitals',
      message: `${name}: ${Math.round(value)}ms (${rating})`,
      level: rating === 'poor' ? 'warning' : 'info',
      data: { delta, id, rating },
    });
  } catch {
    // Sentry uninitialized
  }

  // 2. Dispatch custom event to self-hosted Umami
  try {
    if (typeof window !== 'undefined' && window.umami?.track) {
      window.umami.track('web-vital', {
        metric: name,
        value: Math.round(name === 'CLS' ? value * 1000 : value),
        rating,
      });
    }
  } catch {
    // Umami script blocked or disabled
  }
}

export function initWebVitals(): void {
  // Respect user preference for reduced telemetry / data saver
  if (navigator.connection && (navigator.connection as any).saveData) {
    return;
  }

  onCLS(reportWebVitals);
  onFCP(reportWebVitals);
  onINP(reportWebVitals);
  onLCP(reportWebVitals);
  onTTFB(reportWebVitals);
}

4.2 Application Initialization (src/index.tsx)

Initialize telemetry immediately after error monitoring to capture early paint metrics (FCP / TTFB):

import './lib/sentry';
import './lib/logger';
import { initWebVitals } from './utils/vitals';

// Initialize performance monitoring
initWebVitals();

5. Migration Roadmap: Package Consolidation

Currently, web-vitals is installed individually per application repository. To standardize telemetry across the organization:

  1. Move Telemetry Helper to @dotevolve/error-utils:
  2. Package @dotevolve/error-utils is public on npm/GitHub Packages and already houses Sentry and logging wrappers.
  3. Adding initWebVitals({ umamiTrack?: boolean }) to @dotevolve/error-utils centralizes metric collection logic and ensures updates apply universally.
  4. Package Boundaries:
  5. Public package: @dotevolve/error-utils (safe for all apps and marketing sites).
  6. Internal package: @dotevolve/core (houses proprietary RBAC, permissions, and tenant schemas).