Easily measure performance metrics in JavaScript
—
Measurement functions for the three Core Web Vitals metrics that are part of Google's page experience signals: Cumulative Layout Shift (CLS), Interaction to Next Paint (INP), and Largest Contentful Paint (LCP).
Measures the CLS value for the current page and calls the callback function once the value is ready to be reported. CLS measures the visual stability of a page by quantifying how much visible content shifts unexpectedly.
/**
* Calculates the CLS value and calls callback with layout shift data
* @param callback - Function to receive CLS metric data
* @param opts - Optional configuration for reporting behavior
*/
function onCLS(callback: (metric: CLSMetric) => void, opts?: ReportOpts): void;
interface CLSMetric extends Metric {
name: 'CLS';
entries: LayoutShift[];
}Usage Examples:
import { onCLS } from "web-vitals";
// Track CLS throughout page lifecycle
onCLS((metric) => {
console.log('CLS score:', metric.value);
console.log('Rating:', metric.rating); // 'good', 'needs-improvement', or 'poor'
console.log('Layout shifts:', metric.entries.length);
});
// Report all changes during page lifecycle
onCLS((metric) => {
if (metric.delta > 0) {
console.log('New layout shift detected:', metric.delta);
}
}, { reportAllChanges: true });Important Notes:
Measures the INP value for the current page and calls the callback function with interaction timing data. INP measures responsiveness by tracking the worst interaction latency on the page.
/**
* Calculates the INP value and calls callback with interaction data
* @param callback - Function to receive INP metric data
* @param opts - Optional configuration including duration threshold
*/
function onINP(callback: (metric: INPMetric) => void, opts?: INPReportOpts): void;
interface INPMetric extends Metric {
name: 'INP';
entries: PerformanceEventTiming[];
}
interface INPReportOpts extends ReportOpts {
/** Minimum duration threshold for interactions (default: 40ms) */
durationThreshold?: number;
}Usage Examples:
import { onINP } from "web-vitals";
// Track INP with default threshold
onINP((metric) => {
console.log('INP value:', metric.value + 'ms');
console.log('Rating:', metric.rating);
console.log('Interaction entries:', metric.entries.length);
});
// Custom duration threshold for filtering fast interactions
onINP((metric) => {
console.log('Slow interactions only:', metric.value + 'ms');
}, {
durationThreshold: 100, // Only track interactions >100ms
reportAllChanges: true
});Important Notes:
Measures the LCP value for the current page and calls the callback function with the largest contentful paint timing data. LCP measures loading performance by identifying when the largest content element becomes visible.
/**
* Calculates the LCP value and calls callback with LCP data
* @param callback - Function to receive LCP metric data
* @param opts - Optional configuration for reporting behavior
*/
function onLCP(callback: (metric: LCPMetric) => void, opts?: ReportOpts): void;
interface LCPMetric extends Metric {
name: 'LCP';
entries: LargestContentfulPaint[];
}Usage Examples:
import { onLCP } from "web-vitals";
// Track LCP timing
onLCP((metric) => {
console.log('LCP time:', metric.value + 'ms');
console.log('Rating:', metric.rating);
const lcpEntry = metric.entries[metric.entries.length - 1];
console.log('LCP element:', lcpEntry.element);
console.log('LCP size:', lcpEntry.size);
});
// Report on every LCP candidate change
onLCP((metric) => {
console.log('New LCP candidate:', metric.value + 'ms');
}, { reportAllChanges: true });Important Notes:
reportAllChanges: true, reports every new LCP candidateThe Core Web Vitals metrics use specific performance entry types:
/** Layout shift entries for CLS measurement */
interface LayoutShift extends PerformanceEntry {
value: number;
sources: LayoutShiftAttribution[];
hadRecentInput: boolean;
}
/** Event timing entries for INP measurement */
interface PerformanceEventTiming extends PerformanceEntry {
duration: DOMHighResTimeStamp;
interactionId: number;
}
/** Largest contentful paint entries for LCP measurement */
interface LargestContentfulPaint extends PerformanceEntry {
readonly renderTime: DOMHighResTimeStamp;
readonly loadTime: DOMHighResTimeStamp;
readonly size: number;
readonly id: string;
readonly url: string;
readonly element: Element | null;
}Install with Tessl CLI
npx tessl i tessl/npm-web-vitals