2020-05-09 21:48:52 +02:00
|
|
|
---
|
|
|
|
description: Measure and track page performance using Next.js's build-in performance relayer
|
|
|
|
---
|
|
|
|
|
|
|
|
# Measuring performance
|
|
|
|
|
|
|
|
Next.js has a built-in relayer that allows you to analyze and measure the performance of
|
|
|
|
pages using different metrics.
|
|
|
|
|
|
|
|
To measure any of the supported metrics, you will need to create a [custom
|
|
|
|
App](/docs/advanced-features/custom-app.md) component and define a `reportWebVitals` function:
|
|
|
|
|
|
|
|
```js
|
2020-05-11 03:39:18 +02:00
|
|
|
// pages/_app.js
|
2020-05-09 21:48:52 +02:00
|
|
|
export function reportWebVitals(metric) {
|
|
|
|
console.log(metric)
|
|
|
|
}
|
2020-05-11 03:39:18 +02:00
|
|
|
|
|
|
|
function MyApp({ Component, pageProps }) {
|
|
|
|
return <Component {...pageProps} />
|
|
|
|
}
|
|
|
|
|
|
|
|
export default MyApp
|
2020-05-09 21:48:52 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
This function is fired when the final values for any of the metrics have finished calculating on
|
|
|
|
the page. You can use to log any of the results to the console or send to a particular endpoint.
|
|
|
|
|
|
|
|
The `metric` object returned to the function consists of a number of properties:
|
|
|
|
|
|
|
|
- `id`: Unique identifier for the metric in the context of the current page load
|
|
|
|
- `name`: Metric name
|
|
|
|
- `startTime`: First recorded timestamp of the performance entry (if applicable)
|
|
|
|
- `value`: Value, or duration, of performance entry
|
|
|
|
- `label`: Type of metric (`web-vital` or `custom`)
|
|
|
|
|
|
|
|
There are two types of metrics that are tracked:
|
|
|
|
|
|
|
|
- Web Vitals
|
|
|
|
- Custom metrics
|
|
|
|
|
|
|
|
## Web Vitals
|
|
|
|
|
|
|
|
[Web Vitals](https://web.dev/vitals/) are a set of useful metrics that aim to capture the user
|
|
|
|
experience of a web page. The following web vitals are all included:
|
|
|
|
|
|
|
|
- [Time to First Byte](https://developer.mozilla.org/en-US/docs/Glossary/Time_to_first_byte) (TTFB)
|
|
|
|
- [First Contentful Paint](https://developer.mozilla.org/en-US/docs/Glossary/First_contentful_paint) (FCP)
|
|
|
|
- [Largest Contentful Paint](https://web.dev/lcp/) (LCP)
|
|
|
|
- [First Input Delay](https://web.dev/fid/) (FID)
|
|
|
|
- [Cumulative Layout Shift](https://web.dev/cls/) (CLS)
|
|
|
|
|
|
|
|
You can handle all the results of these metrics using the `web-vital` label:
|
|
|
|
|
|
|
|
```js
|
|
|
|
export function reportWebVitals(metric) {
|
|
|
|
if (metric.label === 'web-vital') {
|
|
|
|
console.log(metric) // The metric object ({ id, name, startTime, value, label }) is logged to the console
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
There's also the option of handling each of the metrics separately:
|
|
|
|
|
|
|
|
```js
|
|
|
|
export function reportWebVitals(metric) {
|
|
|
|
switch (metric.name) {
|
|
|
|
case 'FCP':
|
|
|
|
// handle FCP results
|
|
|
|
break
|
|
|
|
case 'LCP':
|
|
|
|
// handle LCP results
|
|
|
|
break
|
|
|
|
case 'CLS':
|
|
|
|
// handle CLS results
|
|
|
|
break
|
|
|
|
case 'FID':
|
|
|
|
// handle FID results
|
|
|
|
break
|
|
|
|
case 'TTFB':
|
|
|
|
// handle TTFB results
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
A third-party library, [web-vitals](https://github.com/GoogleChrome/web-vitals), is used to measure
|
|
|
|
these metrics. Browser compatibility depends on the particular metric, so refer to the [Browser
|
|
|
|
Support](https://github.com/GoogleChrome/web-vitals#browser-support) section to find out which
|
|
|
|
browsers are supported.
|
|
|
|
|
|
|
|
## Custom metrics
|
|
|
|
|
|
|
|
In addition to the core metrics listed above, there are some additional custom metrics that
|
|
|
|
measure the time it takes for the page to hydrate and render:
|
|
|
|
|
|
|
|
- `Next.js-hydration`: Length of time it takes for the page to start and finish hydrating (in ms)
|
|
|
|
- `Next.js-route-change-to-render`: Length of time it takes for a page to start rendering after a
|
|
|
|
route change (in ms)
|
|
|
|
- `Next.js-render`: Length of time it takes for a page to finish render after a route change (in ms)
|
|
|
|
|
|
|
|
You can handle all the results of these metrics using the `custom` label:
|
|
|
|
|
|
|
|
```js
|
|
|
|
export function reportWebVitals(metric) {
|
|
|
|
if (metric.label === 'custom') {
|
|
|
|
console.log(metric) // The metric object ({ id, name, startTime, value, label }) is logged to the console
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
There's also the option of handling each of the metrics separately:
|
|
|
|
|
|
|
|
```js
|
|
|
|
export function reportWebVitals(metric) {
|
|
|
|
switch (metric.name) {
|
|
|
|
case 'Next.js-hydration':
|
|
|
|
// handle hydration results
|
|
|
|
break
|
|
|
|
case 'Next.js-route-change-to-render':
|
|
|
|
// handle route-change to render results
|
|
|
|
break
|
|
|
|
case 'Next.js-render':
|
|
|
|
// handle render results
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
These metrics work in all browsers that support the [User Timing API](https://caniuse.com/#feat=user-timing).
|
|
|
|
|
|
|
|
## Sending results to analytics
|
|
|
|
|
|
|
|
With the relay function, you can send any of results to an analytics endpoint to measure and track
|
|
|
|
real user performance on your site. For example:
|
|
|
|
|
|
|
|
```js
|
|
|
|
export function reportWebVitals(metric) {
|
|
|
|
const body = JSON.stringify(metric)
|
|
|
|
const url = 'https://example.com/analytics'
|
|
|
|
|
|
|
|
// Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
|
2020-05-12 05:51:26 +02:00
|
|
|
if (navigator.sendBeacon) {
|
|
|
|
navigator.sendBeacon(url, body)
|
|
|
|
} else {
|
2020-05-09 21:48:52 +02:00
|
|
|
fetch(url, { body, method: 'POST', keepalive: true })
|
2020-05-12 05:51:26 +02:00
|
|
|
}
|
2020-05-09 21:48:52 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
> **Note**: If you use [Google Analytics](https://analytics.google.com/analytics/web/), using the
|
|
|
|
> `id` value can allow you to construct metric distributions manually (to calculate percentiles,
|
|
|
|
> etc...).
|
|
|
|
>
|
|
|
|
> ```js
|
|
|
|
> export function reportWebVitals({ id, name, label, value }) {
|
2020-05-11 21:47:56 +02:00
|
|
|
> ga('send', 'event', {
|
|
|
|
> eventCategory:
|
|
|
|
> label === 'web-vital' ? 'Web Vitals' : 'Next.js custom metric',
|
|
|
|
> eventAction: name,
|
|
|
|
> eventValue: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers
|
|
|
|
> eventLabel: id, // id unique to current page load
|
|
|
|
> nonInteraction: true, // avoids affecting bounce rate.
|
|
|
|
> })
|
2020-05-09 21:48:52 +02:00
|
|
|
> }
|
|
|
|
> ```
|
|
|
|
>
|
|
|
|
> Read more about sending results to Google Analytics [here](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics).
|