Getting Started With Web Vitals in Next.js
Tags: frontend, nextjs, javascript, performance
Published: 2020-10-01 13:04:15


In this article, I’ll try to guide you through some examples and definitions that aim to clarify the Web Vitals landscape from a Next.js perspective. Let's dive in!

## How Google Grades Your Websites

User experience is something that Google appreciates when its robots scan your website. They perform checks to make sure your website deserves a good spot on Google's famous search engine results page.

They look for quality indicators such as performance, interactivity, the structure of the pages, responsiveness, safety (e.g. HTTPS), etc.

If you've ever ventured into SEO waters, chances are that at first, you felt overwhelmed by the amount of stuff to worry about.

For this reason, Google came to the rescue with [Web Vitals](https://web.dev/vitals/). They are a new way of analyzing your web pages and checking beforehand for things you might need to address and improve.

Web vitals are a guide made with everybody in mind so that you can easily find out how your website performs. In case there are issues, you should be able to figure out how to address them with ease.


## What Are Web Vitals?

To describe this a bit better, let’s check out a Chrome tool called [Lighthouse](https://developers.google.com/web/tools/lighthouse). If you’ve never heard about it, it’s an open-source automated tool that analyzes and collects quality metrics of web pages; and, yes, it makes use of Web Vitals principles.

The tool is pretty straightforward. On the page that you want to analyze, right-click -> inspect -> look for Lighthouse in the top bar. From there, there are a few options you can choose from:

![Lighouse Preferences](https://blog.appsignal.com/images/blog/2020-09/image1.png)

_Picking up your Lighthouse preferences._

When we run the tool against the AppSignal homepage, we’ll get similar results to these:


![AppSignal metrics](https://blog.appsignal.com/images/blog/2020-09/image2.png)

_AppSignal’s performance metrics._

Here, we’re just showing the metrics related to the _Performance_ section because they embrace more of what Web Vitals do. However, Lighthouse does more.

In short, Web Vitals are divided into six main categories, among which, three are classified as Core Web Vitals to know:

| Metric | Description | Graph reference |
--- | --- | ---
|[Largest Contentful Paint (LCP)](https://web.dev/lcp/)|Here, Google tries to analyze the page loading time as it is perceived by the user, i.e., how long does it take for the main content of your page to load? <br/> If the answer is **\>2.5s** then it needs to be improved. Of course, the lower the better. | ![LCP](https://blog.appsignal.com/images/blog/2020-09/image3.png) |
|[First Input Delay (FID)](https://web.dev/fid/) / [Total Blocking Time (TBT)](https://web.dev/tbt/) | Now, let’s measure how long it takes for the first user interaction to happen within your page. Whether it is through a button click, a page scroll, your browser needs to be ready to respond to those commands in no time, even if the page is not fully loaded. If the time is greater than 100ms, Google asks you to work on that. <br/> Oh, yes, the second indicator, _TBT_, is an auxiliary measurement that analyzes the difference between the _FCP_ and the _TTI_ 一 more on them soon. In the end, they’re birds of a feather.|![FID](https://blog.appsignal.com/images/blog/2020-09/image4.png)|
|[Cumulative Layout Shift (CLS)](https://web.dev/cls/)|This one counts the number of times that stuff or components within a page move (shift) around during the loading. <br/> Have you ever entered a website in which the elements start to “dance” alone before we can even figure out what’s the website about? So, the more you have this, the poorer the experience.|![CLS](https://blog.appsignal.com/images/blog/2020-09/image5.png)|

_Graph’s source: [https://web.dev/vitals/](https://web.dev/vitals/)_

As you may have perceived, The Core Web Vitals are worried about three main points: the loading time, the responsiveness (interactivity), and the stability of the page.

The other three non-Core Web Vitals are:


*   [Time to First Byte (TTFB)](https://web.dev/time-to-first-byte/): it’s the time the browser takes to receive the first byte of page content.
*   [First Contentful Paint (FCP)](https://web.dev/fcp/): calculates the time it takes for any content on the page to first appear on the screen.
*   [Time to Interactive (TTI)](https://web.dev/tti/): it’s a metric of the time from when the page loading gets started until it reliably responds to user input.

Those Vitals are considered more as of auxiliary since they help the Core ones (as well as Lighthouse) to calculate their scores.

Now that you understand a bit more about them, you can refer back to AppSignal’s Lighthouse metrics in the previous figure and recognize what all the indicators, along with their values, are for.

For more details on how Google calculates these metrics behind-the-scenes, please refer to the [official docs](https://web.dev/vitals/).


## Web Vitals With Next.js

Yes, since version 9.4.0, Next.js comes with a built-in layer that understands Web Vitals and allows you to collect such metrics within your Next apps.

Let’s see how it works. First, we need a Next.js app. We’ll be making use of [Yarn](https://yarnpkg.com/) as the package manager.

Run the following command:

```shell
yarn create next-app
```

When it prompts you to fill in the app name, give it the value “web-vitals-next”. Once the creation is finished, open your project with VS Code and run the build command:

```shell
yarn build
```

Finally, run the project with the `yarn dev` command. It’ll be available, by default, at [http://localhost:3000/](http://localhost:3000/).

Introducing Web Vitals to your Next.js app is very simple. You just need to make sure you have a [custom App](https://nextjs.org/docs/advanced-features/custom-app) component (which our yarn creation command already did) and add the following function to your _pages/_app.js_ (or .ts, for TypeScript) file:

```javascript
export function reportWebVitals(metric) {
  metric.label === "web-vital" && console.log(metric);
}
```

This function alone will execute whenever one of the Web Vitals metrics takes place. In the image below, you get to see how it works by logging to the browser’s console, the metrics as they happen:

![Browser console logs](https://blog.appsignal.com/images/blog/2020-09/image6.png)

_Web Vitals metrics logged in the browser’s console._

The structure of a metric object follows this pattern:

![Web Vitals metric object](https://blog.appsignal.com/images/blog/2020-09/image7.png)

_Web Vitals metric object._

It’s important to sort your logs by the `label` value, especially because Next.js also logs some [custom metrics](https://nextjs.org/docs/advanced-features/measuring-performance#custom-metrics) that you may not need.

However, metrics in the browser’s console are not useful. We need to send them to an analytics platform so they can be processed and digested to generate real-time and accountable information.

If you use Google Analytics as such tool, sending the data would be as simple as this:

```javascript
export function reportWebVitals({ id, name, label, value }) {
  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.
  })
}
```

## Sending Metrics to AppSignal

To have a better view of the metrics, let’s take our example to send Web Vitals metrics to AppSignal dashboards as shown below:

![AppSignal dashboards](https://blog.appsignal.com/images/blog/2020-09/image8.png)

Pushing metrics to AppSignal is super easy and fast!

First, you need an [AppSignal account](https://www.appsignal.com/) 一 you have a 30-days free trial. Once you’ve logged in, you’ll be redirected to the startup page, on which you get to choose the language of the app you desire AppSignal to be installed to.

Select _Node.js._ The page that follows will show the instructions to install AppSignal to your Node.js project. Keep the _APPSIGNAL_PUSH_API_KEY_ key, since it’s going to be important later on.

Let’s move on to the app changes. You first need to add the AppSignal npm packages by running:

```shell
yarn add @appsignal/nodejs @appsignal/nextjs
yarn install
```

Pay attention to the log results and check if it finished successfully.

Secondly, AppSignal integration doesn’t work with [Next CLI](https://nextjs.org/docs/api-reference/cli), which is the tool that our example app was built with. That means that we have to create our own [custom server script](https://nextjs.org/docs/advanced-features/custom-server) file and start the application through it.

Go ahead and create a new file called _server.js_ into the app’s root folder. This is the content it must have:

```javascript
const { Appsignal } = require("@appsignal/nodejs");
var http = require('http');

const appsignal = new Appsignal({
  active: true,
  name: "web-vitals-next",
  apiKey: "PUT_YOUR_KEY_HERE", // <<-- Change this!!
});

const {
  getRequestHandler,
  EXPERIMENTAL: { getWebVitalsHandler },
} = require("@appsignal/nextjs");

const url = require("url");
const next = require("next");

const PORT = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });

const handle = getRequestHandler(appsignal, app);
const vitals = getWebVitalsHandler(appsignal);

app.prepare().then(() => {
  http.createServer((req, res) => {
    // Be sure to pass `true` as the second argument to `url.parse`.
    // This tells it to parse the query portion of the URL.
    const parsedUrl = url.parse(req.url, true);
    const { pathname, query } = parsedUrl;

    if (pathname === "/__appsignal-web-vitals") {
      vitals(req, res);
    } else {
      handle(req, res, parsedUrl);
    }
  }).listen(3000, (err) => {
    if (err) throw err;
    console.log("> Ready on http://localhost:3000");
  });
});
```

_Attention: Don’t forget to change the `apiKey` in the code listing to yours._

Note that this is creating a new server with custom settings, but it’s still Next-only based. You can also configure the whole thing with Express if you want to.

Special attention to the `Appsignal` object, which is where AppSignal’s Node.js library tries to connect to the remote API for the first time. If anything goes wrong, this is the place where you should inspect errors.

We’re setting up the app with minimal configs, but you can find a list with all the available config options [here](https://docs.appsignal.com/nodejs/configuration).

Note also that, within the server setup, we’re checking if the pathname equals to _/__appsignal-web-vitals_. This is necessary because the feature works by providing a handler function, which is designed to be used as an endpoint in your application.

This handler function, in turn, is the `reportWebVitals` we’ve seen before. Here’s its new content:

```javascript
export function reportWebVitals(metric) {
  metric.label === "web-vital" && console.log(metric);

  const body = JSON.stringify(metric);
  const url = "/__appsignal-web-vitals";

  // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  (navigator.sendBeacon && navigator.sendBeacon(url, body)) ||
    fetch(url, { body, method: "POST", keepalive: true });
}
```

Alternatively, you may remove the console logging if you don’t want to see the logs in the browser.

The implementation makes use of the [`sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) function with a fallback to a POST request to the AppSignal API. As simple as that!

Finally, let’s go to the _package.json_ file and change our generated scripts:


```json
"scripts": {
   "dev": "node server.js",
   "build": "next build",
   "start": "NODE_ENV=production node server.js"
},
```


Now, start the project locally via `yarn dev` command, get back to the AppSignal setup wizard and click _Next step_. After that, another page will appear with a 60s timer waiting for the requests to arrive from your Next app.

Always good to remember that the usage of this feature is EXPERIMENTAL and may change or be deprecated in future releases. So, be aware!


## Summary

In terms of web quality measuring, Web Vitals represent the best of what’s available in the community. It’s Google’s child and has been largely embraced by the community as a trustworthy metric system to check whether your apps are good enough or still need more work.

Obviously, it’s continuously evolving, that’s why closely watching the [official docs](https://web.dev/vitals/) and repo is always recommended.

Apart from that, since it's not bulletproof, be sure to make other tests and/or check out the results over different platforms to ensure that the quality is what you expected. Good luck!

**P.S. If you liked this post, [subscribe to our new JavaScript Sorcery list](https://blog.appsignal.com/javascript-sorcery) for a monthly deep dive into more magical JavaScript tips and tricks.**

**P.P.S. If you'd love an all-in-one APM for Node or you're already familiar with AppSignal, go and [check out AppSignal for Node.js](https://www.appsignal.com/nodejs).**