grab-vitals
v0.2.0
Published
A wrapper around Google's web-vitals package to simplify collection and transmission of web vitals to an endpoint.
Downloads
3
Readme
grab-vitals
grab-vitals
is a small script I wrote to gather and beacon web vitals gathered by Google's web-vitals
package. The following metrics are collected:
- Cumulative Layout Shift (CLS)
- Largest Contentful Paint (LCP)
- First Input Delay (FID)
- First Contentful Paint (FCP)
- Time to First Byte (TTFB)
I originally wrote this as part of the codebase for my personal website, but decided to break this out into its own separate module for maintainability and for the off-chance that someone might find it useful. I hope maybe that someone is you!
Installation
To use grab-vitals
in your app, install it along the web-vitals
package via npm as production dependencies:
npm install grab-vitals --save
You should not need to install web-vitals
as a dependency in your app if you use this package. If you're already using the web-vitals
in your own code, uninstall that dependency from your project and let this package handle reporting entirely for you, if possible. Otherwise, you may end up shipping two copies of web-vitals
.
Basic usage
The simplest usage of grab-vitals
looks something like this:
window.addEventListener("load", () => {
import("grab-vitals").then(({ grabVitals }) => {
grabVitals("https://metrics.compuhyperglobalmega.net/collect");
});
});
In this example, we wait until the window
's load
event to dynamically import the grab-vitals
package. grab-vitals
provides a grabVitals
named function that accepts a URL endpoint where the metrics will be sent via navigator.sendBeacon
. Once this function is run, metrics are collected as they become available, and are eventually sent to the collection endpoint as a JSON-encoded object. That object has the following shape:
{
pathName,
metrics: [
{
time,
metric,
value
},
// ...
]
}
The object contains the following data:
pathName
contains the value ofdocument.location.pathName
.metrics
is an array of objects containing info on each metric. Each object in the array contains the following:time
contains the value ofperformance.now()
roughly representing when the metric was captured.metric
is a string containing the metric name (e.g.,"cls"
,"fid"
, et. al).value
is metric's value.
Depending on how you configure grabVitals
, there may be some other object members, but this represents the bare minimum that grab-vitals
will send to a collection endpoint.
Once ready, grab-vitals
will send the JSON-encoded object in a POST request to the given endpoint. From there, it's up to you to write a back end that decodes that JSON (e.g., like PHP's json_decode
) and stores it somewhere for later analysis.
Advanced configuration
The grabVitals
function takes the following arguments in the order in which they're listed:
endpoint
: Required. The URL to the collection endpoint where a POST request containing the metrics will be sent. Default:undefined
.getFIDLongTasks
: Optional. When set totrue
,grab-vitals
will attempt to contextualize First Input Delay by gathering data on long tasks from the Long Tasks API within a given window of time around the point at which the input delay was captured. This can be useful for contextualizing input delays, since a user's first input can vary wildly across different users. Long task data won't be collected in browsers that don't support the Long Tasks API. Default:true
.longTaskWindow
: Optional. The window of time, in milliseconds, in whichgrab-vitals
will search around the time of the input delay to look for long tasks. The window puts the input delay squarely in the middle, so a value of5000
will mean thatgrab-vitals
will look 2.5 seconds before and after the time of the input delay's capture to look for long tasks. Default:2500
.additionalMetrics
: Optional. Additional metrics to transmit along with web vitals. Read on to see an example of this. Default:undefined
.
Adding additional metrics
You may want to capture more stuff than just web vitals. Whatever that stuff is, grab-vitals
offers a way for you to transmit that information to your collection endpoint by way of the additionalMetrics
parameter.
additionalMetrics
does not expect data in any particular format, but storing those metrics in an array or an object is probably the best approach. For example, let's say you wanted to transmit characteristics of the user's current network connection along with the collected web vitals:
window.addEventListener("load", () => {
const metrics = [];
if ("connection" in navigator) {
metrics.push(
{
ect: navigator.connection.effectiveType
}, {
downlink: navigator.connection.downlink
}, {
rtt: navigator.connection.rtt
}
);
}
import("grab-vitals").then(({ grabVitals }) => {
grabVitals("https://metrics.compuhyperglobalmega.net/collect", true, 2500, metrics);
});
});
All that matters when you structure collection of additional metrics is that the values must be encodable by JSON.stringify
. When additionalMetrics
is supplied, a new top-level object member named additionalMetrics
will be created in the JSON payload that gets sent to your collection endpoint.
Now it's just up to you write a back-end script to collect everything!
Handling metrics in your application back end
Once metrics start arriving at your designated endpoint, you need to decode the JSON on the back end and figure out what to do with it. Here's a barebones PHP example:
<?php
// Make sure this is a POST request
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Takes raw data from the request.
$json = file_get_contents("php://input");
// Decode the JSON into something PHP can work with.
$data = json_decode($json);
// Access the path name:
$pathName = $data->pathName;
// Access web vitals
foreach ($data->metrics as $metric) {
// Process and store metrics here. Access object members like so:
$metricTime = $metric->time; // The time elapsed since the time origin.
$metricName = $metric->name; // The string name of the metric (e.g., "FID").
$metricValue = $metric->value; // The value of the associated metric (e.g., 22.3).
// If long tasks are collected for FID, they'll be available in an array:
if ($metricName === "FID") {
// This assumes you're collecting long tasks. Always check to make sure
// the value you're working with exists instead of just assuming it does!
$fidLongTasks = $metric->longTasks;
}
}
}
?>
However you do this on your application back end depends on the language you're using, but this should give you a barebones idea of how to work with the data grab-vitals
sends.
Contributing
I mostly did this as a proof of concept to try and simplify my metrics collection efforts. If you find it useful, great! If you have ideas for contributing, that depends on what you want to contribute. If you want to extend this to do something specific to your situation, just fork the package. If you find bugs or have a novel feature request, file an issue!