A (send) beacon of hope

27 February 2021

Web Vitals from Google allow developers to easily collect and observe important client-side metrics in their web app very easily. Even better, there is a widely supported navigator.sendBeacon function which allows your to asynchronously POST data to an endpoint of your choosing. So for example, in Next.js you can do:

export function reportWebVitals(metric) {
  const body = JSON.stringify(metric);
  const url = "https://mywebsite.com/analytics";

  // Assuming you don't care about IE support, you can just do this
  navigator.sendBeacon(url, body);
}

The reportWebVitals function gets called every time a metric is generated and allows you to send metrics to an analytics service - giving you rich, real-user data for free!

But what's going on with JSON.stringify? If this is going to POST to a web server, why can't we just fire the data off with the content type application/json and do const { name, value } = req.body on the backend?

This is because sendBeacon uses the text/plain Content Type by default.

If you want to be able to do something like const { name, value } = req.body on the backend without JSON.parse then you need to override the default behaviour of sendBeacon by using a Blob as the payload. When we send a Blob the sendBeacon request uses the Content Type from the Blob, rather than forcing text/plain.

To get the desired effect, change your code to this:

export function reportWebVitals(metric) {
  const body = JSON.stringify(metric);
  const url = "https://mywebsite.com/analytics";
  const blob = new Blob([body], { type: "application/json" });

  // Again, assuming you don't care about IE support
  navigator.sendBeacon(url, blob);
}