Custom analytics
Send browser performance data to a first-party analytics endpoint.
Next.js 16+ · Hook
Reports Core Web Vitals through a callback so an application can send performance measurements to its analytics service. Use it in a Client Component and keep the callback reference stable.
This Client Component hook receives performance metrics as they become available. Its callback can inspect the metric and forward it to an analytics service; keeping the callback reference stable prevents duplicate reporting. A small dedicated component limits the client boundary.
useReportWebVitals(callback)| Name | Type | Description |
|---|---|---|
callback | (metric) => void | Callback invoked with each available Web Vitals metric. |
No documented return value; the hook registers a metric-reporting callback.
'use client'
import { useReportWebVitals } from 'next/web-vitals'
export function WebVitals() {
useReportWebVitals((metric) => {
const body = JSON.stringify(metric)
const url = '/api/analytics'
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body)
} else {
fetch(url, { body, method: 'POST', keepalive: true })
}
})
return null
}Place this Client Component in the root layout to send collected metrics to an analytics endpoint.
Send browser performance data to a first-party analytics endpoint.
Inspect individual measurements such as LCP or CLS and handle them differently.
Measure real-user performance across navigations and page loads.
useReportWebVitals do?It is a Client Component hook imported from next/web-vitals. It invokes a callback with available Web Vitals metrics, which can then be sent to an analytics service.
Follow-up: What prevents duplicate reports? Keep the callback function reference unchanged between renders.
The callback receives a metric object. Documented fields include an identifier for the current page load, the metric name, and the navigation type that triggered collection.
Follow-up: How could the callback distinguish metrics? Inspect metric.name, whose documented values include metrics such as TTFB, FCP, LCP, FID, and CLS.
Create a separate Client Component with the directive before imports, import useReportWebVitals from next/web-vitals, and render that component from the root layout. The callback can use navigator.sendBeacon() or a keepalive fetch() request to transmit the serialized metric.
Follow-up: Why use a separate component? It keeps the client boundary limited to the component and its imports, which is the documented performant approach.
Web VitalsCore Web Vitalsnext/web-vitalsnavigator.sendBeacon