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 component and define a reportWebVitals function:

  1. // pages/_app.js
  2. export function reportWebVitals(metric) {
  3. console.log(metric)
  4. }
  5. function MyApp({ Component, pageProps }) {
  6. return <Component {...pageProps} />
  7. }
  8. export default MyApp

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 in milliseconds (if applicable)
  • value: Value, or duration in milliseconds, of the 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 are a set of useful metrics that aim to capture the user experience of a web page. The following web vitals are all included:

You can handle all the results of these metrics using the web-vital label:

  1. export function reportWebVitals(metric) {
  2. if (metric.label === 'web-vital') {
  3. console.log(metric) // The metric object ({ id, name, startTime, value, label }) is logged to the console
  4. }
  5. }

There’s also the option of handling each of the metrics separately:

  1. export function reportWebVitals(metric) {
  2. switch (metric.name) {
  3. case 'FCP':
  4. // handle FCP results
  5. break
  6. case 'LCP':
  7. // handle LCP results
  8. break
  9. case 'CLS':
  10. // handle CLS results
  11. break
  12. case 'FID':
  13. // handle FID results
  14. break
  15. case 'TTFB':
  16. // handle TTFB results
  17. break
  18. default:
  19. break
  20. }
  21. }

A third-party library, web-vitals, is used to measure these metrics. Browser compatibility depends on the particular metric, so refer to the 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:

  1. export function reportWebVitals(metric) {
  2. if (metric.label === 'custom') {
  3. console.log(metric) // The metric object ({ id, name, startTime, value, label }) is logged to the console
  4. }
  5. }

There’s also the option of handling each of the metrics separately:

  1. export function reportWebVitals(metric) {
  2. switch (metric.name) {
  3. case 'Next.js-hydration':
  4. // handle hydration results
  5. break
  6. case 'Next.js-route-change-to-render':
  7. // handle route-change to render results
  8. break
  9. case 'Next.js-render':
  10. // handle render results
  11. break
  12. default:
  13. break
  14. }
  15. }

These metrics work in all browsers that support the User Timing API.

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:

  1. export function reportWebVitals(metric) {
  2. const body = JSON.stringify(metric)
  3. const url = 'https://example.com/analytics'
  4. // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  5. if (navigator.sendBeacon) {
  6. navigator.sendBeacon(url, body)
  7. } else {
  8. fetch(url, { body, method: 'POST', keepalive: true })
  9. }
  10. }

Note: If you use Google Analytics, using the id value can allow you to construct metric distributions manually (to calculate percentiles, etc…).

  1. export function reportWebVitals({ id, name, label, value }) {
  2. // Use `window.gtag` if you initialized Google Analytics as this example:
  3. // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics/pages/_document.js
  4. window.gtag('event', name, {
  5. event_category:
  6. label === 'web-vital' ? 'Web Vitals' : 'Next.js custom metric',
  7. value: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers
  8. event_label: id, // id unique to current page load
  9. non_interaction: true, // avoids affecting bounce rate.
  10. })
  11. }

Read more about sending results to Google Analytics here.

TypeScript

If you are using TypeScript, you can use the built-in type NextWebVitalsMetric:

  1. // pages/_app.tsx
  2. import type { AppProps, NextWebVitalsMetric } from 'next/app'
  3. function MyApp({ Component, pageProps }: AppProps) {
  4. return <Component {...pageProps} />
  5. }
  6. export function reportWebVitals(metric: NextWebVitalsMetric) {
  7. console.log(metric)
  8. }
  9. export default MyApp