0
# Core Web Vitals
1
2
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).
3
4
## Capabilities
5
6
### Cumulative Layout Shift (CLS)
7
8
Measures the [CLS](https://web.dev/articles/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.
9
10
```typescript { .api }
11
/**
12
* Calculates the CLS value and calls callback with layout shift data
13
* @param callback - Function to receive CLS metric data
14
* @param opts - Optional configuration for reporting behavior
15
*/
16
function onCLS(callback: (metric: CLSMetric) => void, opts?: ReportOpts): void;
17
18
interface CLSMetric extends Metric {
19
name: 'CLS';
20
entries: LayoutShift[];
21
}
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import { onCLS } from "web-vitals";
28
29
// Track CLS throughout page lifecycle
30
onCLS((metric) => {
31
console.log('CLS score:', metric.value);
32
console.log('Rating:', metric.rating); // 'good', 'needs-improvement', or 'poor'
33
console.log('Layout shifts:', metric.entries.length);
34
});
35
36
// Report all changes during page lifecycle
37
onCLS((metric) => {
38
if (metric.delta > 0) {
39
console.log('New layout shift detected:', metric.delta);
40
}
41
}, { reportAllChanges: true });
42
```
43
44
**Important Notes:**
45
- CLS should be continually monitored throughout the entire page lifespan
46
- The callback may be called multiple times as new layout shifts occur
47
- Always reports when page visibility changes to hidden
48
- Threshold: ≤0.1 (good), 0.1-0.25 (needs improvement), >0.25 (poor)
49
50
### Interaction to Next Paint (INP)
51
52
Measures the [INP](https://web.dev/articles/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.
53
54
```typescript { .api }
55
/**
56
* Calculates the INP value and calls callback with interaction data
57
* @param callback - Function to receive INP metric data
58
* @param opts - Optional configuration including duration threshold
59
*/
60
function onINP(callback: (metric: INPMetric) => void, opts?: INPReportOpts): void;
61
62
interface INPMetric extends Metric {
63
name: 'INP';
64
entries: PerformanceEventTiming[];
65
}
66
67
interface INPReportOpts extends ReportOpts {
68
/** Minimum duration threshold for interactions (default: 40ms) */
69
durationThreshold?: number;
70
}
71
```
72
73
**Usage Examples:**
74
75
```typescript
76
import { onINP } from "web-vitals";
77
78
// Track INP with default threshold
79
onINP((metric) => {
80
console.log('INP value:', metric.value + 'ms');
81
console.log('Rating:', metric.rating);
82
console.log('Interaction entries:', metric.entries.length);
83
});
84
85
// Custom duration threshold for filtering fast interactions
86
onINP((metric) => {
87
console.log('Slow interactions only:', metric.value + 'ms');
88
}, {
89
durationThreshold: 100, // Only track interactions >100ms
90
reportAllChanges: true
91
});
92
```
93
94
**Important Notes:**
95
- Requires PerformanceEventTiming API support
96
- Default threshold is 40ms (filters out very fast interactions)
97
- First input is always observed regardless of duration
98
- Callback may be called multiple times as worse interactions occur
99
- Threshold: ≤200ms (good), 200-500ms (needs improvement), >500ms (poor)
100
101
### Largest Contentful Paint (LCP)
102
103
Measures the [LCP](https://web.dev/articles/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.
104
105
```typescript { .api }
106
/**
107
* Calculates the LCP value and calls callback with LCP data
108
* @param callback - Function to receive LCP metric data
109
* @param opts - Optional configuration for reporting behavior
110
*/
111
function onLCP(callback: (metric: LCPMetric) => void, opts?: ReportOpts): void;
112
113
interface LCPMetric extends Metric {
114
name: 'LCP';
115
entries: LargestContentfulPaint[];
116
}
117
```
118
119
**Usage Examples:**
120
121
```typescript
122
import { onLCP } from "web-vitals";
123
124
// Track LCP timing
125
onLCP((metric) => {
126
console.log('LCP time:', metric.value + 'ms');
127
console.log('Rating:', metric.rating);
128
129
const lcpEntry = metric.entries[metric.entries.length - 1];
130
console.log('LCP element:', lcpEntry.element);
131
console.log('LCP size:', lcpEntry.size);
132
});
133
134
// Report on every LCP candidate change
135
onLCP((metric) => {
136
console.log('New LCP candidate:', metric.value + 'ms');
137
}, { reportAllChanges: true });
138
```
139
140
**Important Notes:**
141
- Stops observing once user interacts with the page or page becomes hidden
142
- Multiple candidates may be reported before final LCP is determined
143
- With `reportAllChanges: true`, reports every new LCP candidate
144
- Threshold: ≤2.5s (good), 2.5-4s (needs improvement), >4s (poor)
145
146
## Performance Entry Types
147
148
The Core Web Vitals metrics use specific performance entry types:
149
150
```typescript { .api }
151
/** Layout shift entries for CLS measurement */
152
interface LayoutShift extends PerformanceEntry {
153
value: number;
154
sources: LayoutShiftAttribution[];
155
hadRecentInput: boolean;
156
}
157
158
/** Event timing entries for INP measurement */
159
interface PerformanceEventTiming extends PerformanceEntry {
160
duration: DOMHighResTimeStamp;
161
interactionId: number;
162
}
163
164
/** Largest contentful paint entries for LCP measurement */
165
interface LargestContentfulPaint extends PerformanceEntry {
166
readonly renderTime: DOMHighResTimeStamp;
167
readonly loadTime: DOMHighResTimeStamp;
168
readonly size: number;
169
readonly id: string;
170
readonly url: string;
171
readonly element: Element | null;
172
}
173
```