completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

Choosing a RUM tool

What commercial and open-source RUM tools offer, and the questions to ask before you pick one. Five real SDKs on one shop in Chrome: how much each adds to the page, what each sent, and which reports were lost when the visitor left.

By the end, you can
  • 01
    Say who runs each jobTell your own pipeline, an open-source tool you run, and a hosted service apart, and what each kind of hosted tool is built for.
  • 02
    Test a tool before you trust itWeigh its script, time it, and check that its last report arrives when a visitor follows a link or switches tabs.
  • 03
    Ask the buying questionsFind what's billed, what the tool does until you change it, what a free tier leaves out, and who keeps the data.

What a RUM tool does for you

Leaf and Kettle's team runs its own RUM pipeline now. RUM, real-user monitoring, means measuring the visits of real people. When Asha leaves the mint tea page, her visit sends a beacon. The shop's endpoint checks it, storage keeps it, and the dashboard shows the p75. Building a RUM pipeline built every stage.

It works. But the team has to run it. Someone keeps the server up, makes room as the file of events grows, and fixes the dashboard when it breaks. So one morning, someone asks: should we keep running our own, or pay for a RUM tool?

There are many RUM tools. So how do you choose? And once a tool is on your page, how do you know it sends what it says?

Four jobs, three ways to share them

Every RUM tool does the same four jobs as the team's pipeline:

  • Measure in the page. Work out LCP, INP and the other metrics, the way the web-vitals library does.
  • Send each result out of the page as a report, a small message with a metric in it. It goes to a collector, the server address that receives reports. Sending performance data showed how.
  • Store every report, like the pipeline's file of raw events.
  • Show the results: percentiles, a dashboard and alerts.

Tools differ in who does which job. A tool's code for the page is its SDK, short for software development kit. Past the SDK, there are three ways to share the work:

  • Build your own. A small library like web-vitals measures. Your code sends, and your servers store and show. That's the team's pipeline today.
  • Run an open-source tool yourself. The tool's SDK measures and sends. You run its collector, its storage and its dashboards on your own servers. Open source means anyone may read the code, change it and run it.
  • Pay for a hosted service. The SDK measures and sends. The company that makes the tool, the vendor, stores and shows the data on its own servers. That's what hosted means.

Here are the three ways side by side:

Who does each job. Your own pipeline: Measure, web-vitals; Send, You; Store, You; Show, You. Open source you run: Measure, The SDK; Send, The SDK; Store, You; Show, You. A hosted service: Measure, The SDK; Send, The SDK; Store, The vendor; Show, The vendor.MeasureSendStoreShowYour own pipelineweb-vitalsYouYouYouOpen source you runThe SDKThe SDKYouYouA hosted serviceThe SDKThe SDKThe vendorThe vendor
Read each row across. Shaded: the jobs your own team runs. Lower down, you run less, and the tool decides more.
Real-life analogyCooking at home, a meal kit, or a restaurant

You want dinner. You can cook at home: you buy everything, cook it and wash up, and it's exactly how you like it. You can order a meal kit: the recipe and the ingredients arrive in a box, but you still cook and wash up. Or you can eat at a restaurant. Someone else does all of it, you choose from their menu, and you pay for every plate.

In real life: Cooking at home
In RUM tools: Your own pipeline: web-vitals, your endpoint and your storage
In real life: A meal kit's recipe and ingredients
In RUM tools: An open-source SDK and its collector
In real life: Cooking and washing up yourself
In RUM tools: Running the collector, storage and dashboards on your servers
In real life: Eating at a restaurant
In RUM tools: A hosted service: the vendor stores and shows the data
In real life: Paying for every plate
In RUM tools: Paying per session, page view or event

None of the three is always best. The rest of this lesson shows what each one costs you.

Four kinds of tool

Hosted tools come in three kinds, by what else they do. Open-source tools you run make a fourth. The picture names a few tools of each kind, and the next four sections take one kind each.

Four kinds of RUM tool: Speed specialists, such as SpeedCurve, Akamai mPulse; Observability platforms, such as Sentry, Datadog RUM, recorded by the course; Hosting providers, such as Vercel Speed Insights, Cloudflare Web Analytics; Open source, such as Grafana Faro, Elastic APM RUM, web-vitals, recorded by the course.Speed specialistsSpeedCurve, Akamai mPulseObservability platformsSentry, Datadog RUMHosting providersVercel Speed Insights, Cloudflare Web AnalyticsOpen sourceGrafana Faro, Elastic APM RUM, web-vitals
Four kinds of RUM tool, with examples. Shaded: the two kinds the course installed and tested. The other two need an account with the vendor.

The course's test

To see what tools really do, the course put five of them on a copy of the mint tea page, one at a time:

  • web-vitals, with a six-line do-it-yourself setup;
  • Sentry and Datadog RUM, two observability platforms;
  • Grafana Faro and Elastic APM RUM, two open-source tools.

Each had its smallest real setup. A server on the course's computer played every collector, and noted when each report arrived. Then Chrome made 5 visits per tool for each of two endings, 50 visits in all. Each visit went like this:

  1. Load the page, and wait one and a half seconds.
  2. Click Add to cart. Its click handler keeps the main thread busy for 150 ms. The main thread is the one thread that runs the page's JavaScript and answers clicks. So every visit has a slow click, and an INP to report.
  3. Wait one second. Then either follow a link to another page, or switch to another tab and leave this one open.
  4. Close the tab 5 s later.

Every tool goes on the page in a line or two. Here is Elastic's whole setup, as the course used it:

Elastic's whole setup: two linesJavaScript
import { init } from "@elastic/apm-rum";
init({ serviceName: "shop", serviceVersion: "1.0.0", serverUrl: COLLECTOR + "/elastic" });
  • Line 1 loads the init function from Elastic's package, @elastic/apm-rum.
  • Line 2 starts the SDK. serviceName names the shop. serverUrl is the collector's address.
  • COLLECTOR stands for the course's own server, filled in when the file was built.

Two lines. But what do they add to every page? When do they send? Does every report arrive? The five tools gave very different answers. One of them lost a report every time a visitor followed a link.

The main idea

Every RUM tool measures, sends, stores and shows. Tools differ in who runs each job, what they add to every page, what they cost, and who keeps the data. Pick by what you need. Then test it: check that the last report arrives when visitors leave.

Dedicated performance RUM tools

Some tools do one thing, page speed, like a shop that sells only tea.

SpeedCurve is one. Its setup guide has you add a small script, lux.js, to every page. It says to put the script "as early in the page as possible, i.e. at the top of the HEAD element." It says this "ensures that we are able to collect as much data as possible for each page."

The same guide says: "The lux.js script is loaded asynchronously so it won't harm the performance of your site." What does that mean?

It means the script tag has the async attribute. MDN says such a script "will be fetched in parallel to parsing and evaluated as soon as it is available." So the browser keeps reading the HTML while the script downloads. But the script still downloads, and it still runs on the main thread, the same thread that answers clicks. Async lowers a script's cost, but it doesn't remove it.

SpeedCurve also runs synthetic tests: lab tests of your pages, on a schedule. A page label is a name for a kind of page, like "home" or "product". The guide says that matching labels in both "enables correlation between RUM and synthetic data". The lab numbers and the field numbers for one kind of page then sit side by side.

Akamai's mPulse is another speed specialist. It's built on boomerang, an open-source library. boomerang's README says: "boomerang is a JavaScript library that measures the page load time experienced by real users, commonly called RUM (Real User Measurement)."

Akamai's own version, the README says, "contains a few mPulse-specific plugins". A plugin is an add-on that gives a library one more job.

Both tools need an account with the vendor, so the course didn't install them. But you can test them the same way as the other five: weigh what they add to the page, and check that their last report arrives.

Observability platforms

If a speed specialist is a tea shop, an observability platform is a supermarket. Speed is one aisle of many.

Observability means being able to see what your whole system is doing: its errors, its slow server calls, its logs. Sentry and Datadog sell tools for all of that, and RUM is one part. Sentry's docs describe what it gives you as information "about errors and performance".

Datadog's billing docs list what its RUM collects. That's every page a visitor sees, the files each page loads, errors in the page, and long tasks.

What do you get for all that? A thread from the page to the server. Datadog's docs say that with its APM integration, "you can tie any XHR or Fetch request to its corresponding backend trace." APM, application performance monitoring, is its tool for servers. A trace is the record of one request's trip through your servers. So a slow click on the page can lead straight to the slow server call behind it.

What the SDK adds to every page

All that comes at a price, paid by every visitor. Each visitor's browser downloads and runs the SDK before it can measure anything. A big SDK is like a guest who brings three suitcases for one night: you have to make room for all of them.

To compare fairly, the course built each tool's smallest setup the same way. esbuild 0.25.11, a build tool, put each setup and everything it imports into one file. That's called bundling. It also minified the file, taking out spaces and shortening names. Then the course compressed it with brotli, as a server would. Here is Datadog's, next to web-vitals':

What Datadog adds, next to web-vitalsPop out in the code editor (opens in a new tab)JavaScript
// Each tool's smallest setup, bundled, minified and brotli-compressed, in bytes.const webVitals = 3141;const datadog = 73923; console.log(`Datadog RUM adds ${datadog.toLocaleString("en-US")} compressed bytes to the page.`);console.log(`That's ${(datadog / webVitals).toFixed(1)} times as much as web-vitals.`);
  • Lines 2 and 3 are recorded: 3,141 bytes for web-vitals, and 73,923 bytes for Datadog RUM.
  • Line 5 prints Datadog RUM adds 73,923 compressed bytes to the page.
  • Line 6 divides one by the other. It prints That's 23.5 times as much as web-vitals.

Here are all five tools, measured the same way:

Brotli-compressed bundle of each SDK's smallest setup: web-vitals 3,141 B, Sentry 49,114 B, Datadog RUM 73,923 B, Grafana Faro 37,445 B, Elastic APM RUM 20,924 B.web-vitals3,141 BSentry49,114 BDatadog RUM73,923 BGrafana Faro37,445 BElastic APM RUM20,924 B
Each tool's smallest setup, bundled, minified and compressed with brotli. Shaded: web-vitals, the do-it-yourself setup. A visitor's browser downloads the whole bar before the tool measures anything.

Bytes are half the cost; the code also has to run. So the course put a mark with performance.mark() on each side of the SDK's script tag. The tag was a plain one, without async. So the browser stopped reading the HTML until the script had downloaded and run. The gap between the marks is the whole cost.

Each tool had 10 visits. The median, the middle value of those visits, was 13.1 ms for web-vitals. It was 26.8 ms for Sentry, and 32.7 ms for Datadog. The script came from the same computer. On a real network, the download can take far longer.

Why it matters: a RUM tool slows down the page it measures. Before you pick one, build its smallest setup and weigh it, the way the course did.

Hosting-provider analytics

Your site lives on someone's servers: your hosting provider. Some hosts can measure your visitors too, in the same dashboard where you put your site live.

Vercel is one. Its docs say: "Vercel Speed Insights provides you with a detailed view of your website's performance metrics, based on Core Web Vitals". To start, you add one package to your site, @vercel/speed-insights. The numbers then appear in your Vercel project's dashboard.

Speed Insights comes in two tiers. Vercel's pricing docs list what each one shows:

  • The free tier shows the Real Experience Score, or RES, only. Vercel says RES "uses real data points collected from your users' devices."
  • Speed Insights Plus "adds all Core Web Vitals, detailed breakdowns, and Drains." Drains, Vercel says, "let you forward observability data from your applications to external services".

Cloudflare has one too. Its docs say: "Cloudflare Web Analytics provides free, privacy-first analytics for your website without changing your DNS or using Cloudflare's proxy." So your site can stay where it is. You need a Cloudflare account and a small script on your page. That script, the docs say, "collects metrics using the Performance API".

These are the quickest tools to start with. They're also the ones where the most is decided for you.

Real-life analogyA furnished flat

A furnished flat is ready the day you move in: bed, table, kettle. But the landlord chose them, and you can't knock down a wall. When you move out, the furniture stays behind. An unfurnished flat starts empty, and everything you put in it is yours to choose and to take with you.

In real life: Moving into a furnished flat
In RUM tools: Turning on your host's analytics
In real life: The landlord's choice of furniture
In RUM tools: The metrics, splits and charts the host chose
In real life: The walls you can't move
In RUM tools: The settings you can't change
In real life: Leaving the furniture behind when you move out
In RUM tools: Leaving the data behind when you change host
In real life: An unfurnished flat
In RUM tools: Your own pipeline, where everything is yours to choose and to take

So before you rely on your host's analytics, look around the flat:

  • What can I split by? On Vercel's free tier, the breakdowns are by path and route, with only the Great and Needs Improvement entries. Plus adds the Poor ones, countries, and element selectors.
  • Can I get the raw data out? On Vercel, that's Drains, which come with Plus.
  • What if I change host? Speed Insights lives in a Vercel project. Cloudflare's works without moving your site.

Why it matters: the quickest tool to turn on can be the hardest to leave. Check the walls before you move in.

Open-source and self-hosted options

Back to the meal kit. An open-source tool hands you the recipe and the ingredients: an SDK for the page, and a collector. You do the cooking: you run them on your own servers.

Grafana Faro is one. Grafana calls its SDK "a highly configurable open source JavaScript agent". It collects "performance metrics, logs, exceptions, events, and traces." And it sends them to "a collector that you can run yourself or that runs in Grafana Cloud."

Faro's README names the collector you'd run, Grafana Alloy. Grafana's page says the collector saves the data into Loki and Tempo, two of Grafana's storage tools, and you look at it in Grafana.

Elastic APM RUM is another. Elastic's docs say its agents "work in conjunction with the APM Server, Elasticsearch, and Kibana." Those are three more pieces for you to run. Elastic's page on the APM Server says it "receives performance data from APM agents" and turns it into Elasticsearch documents. Elasticsearch stores them, and you look at them in Kibana.

Every tool's code comes with a licence: the legal note that says what you may do with it. Faro's is Apache-2.0 and Elastic's agent's is MIT. Both are open-source licences, so you may use the code, change it and run it yourself.

Here's a surprise: every SDK the course used has an open-source licence. Sentry's is MIT, and Datadog's is Apache-2.0. With those two, what you pay for is the service behind the SDK: the collector, the storage and the dashboards.

Or you can cook at home: web-vitals, whose licence is Apache-2.0, and your own pipeline. This is the whole do-it-yourself setup the course bundled:

web-vitals and one beaconJavaScript
import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals";const queue = [];for (const on of [onCLS, onFCP, onINP, onLCP, onTTFB]) on((metric) => queue.push({ name: metric.name, value: metric.value, id: metric.id }));addEventListener("visibilitychange", () => {  if (document.visibilityState === "hidden" && queue.length) navigator.sendBeacon(COLLECTOR + "/diy", JSON.stringify(queue.splice(0)));});
  • Line 1 loads five functions from web-vitals, one per metric.
  • Line 2 makes an empty queue: a list of reports waiting to be sent.
  • Line 3 gives each function the same small callback. When a metric is ready, the callback adds its name, value and id to the queue.
  • Lines 4 to 6 send the whole queue in one beacon, the moment the page becomes hidden. Line 5 checks that the queue isn't empty first.
  • queue.splice(0) takes every report out of the queue, so none is sent twice.

It came to 9,183 bytes, and 3,141 with brotli. But free to use isn't free to run. A meal kit still needs your kitchen and your evening: you run the servers, keep them going, and pay for them. In return, the data never leaves your own servers.

What reached the collector

A tool is only as good as the reports that reach its collector. And the hardest report to get out is the last one.

Remember why. INP and CLS can change until the visitor leaves, so the web-vitals library hands over their final values as the page is hidden. That's the very moment the page may be about to disappear.

In the course's test, every visit clicked Add to cart. Each visit's own Event Timing entries showed that click taking at least 152 ms. So every visit had an INP to send. Did it arrive?

Try it first. Pick a tool, and how the visit ended. Each row is one visit, and each box is one report. A minus sign means the report arrived before the visit ended:

Playground: what each SDK sent
VisitReports
  1. Visit 1INP arrived+29 ms338 BLCP INP CLS FCP TTFB
  2. Visit 2INP arrived+20 ms339 BLCP INP CLS FCP TTFB
  3. Visit 3INP arrived+22 ms339 BLCP INP CLS FCP TTFB
  4. Visit 4INP arrived+121 ms324 BLCP INP CLS FCP TTFB
  5. Visit 5INP arrived+20 ms339 BLCP INP CLS FCP TTFB
Try it yourself
web-vitals, follow a link: INP arrived in 5 of 5 visits

A typical visit sent 1 report, 339 bytes in all, before its tab closed. Each row below is one visit. Each box is one report: when it arrived, before (−) or after (+) the visit ended, its size, and the metrics in it.

Recorded in Chrome 156 on an Apple M1 Pro: web-vitals 6.2.3, licensed Apache-2.0. In every visit, the Add to cart click kept the main thread busy for 150 ms. The tab closed 5 s after the visit ended.

Here are the visits that followed a link, counted:

Following a link: did INP arrive?Pop out in the code editor (opens in a new tab)JavaScript
// In how many of 5 visits that followed a link did each tool's INP arrive?const inpArrived = {  "web-vitals": 5,  Sentry: 5,  "Datadog RUM": 5,  "Grafana Faro": 5,  "Elastic APM RUM": 0,}; for (const [tool, count] of Object.entries(inpArrived)) console.log(`${tool}: INP arrived in ${count} of 5 visits.`);
  • Line 1 asks the question.
  • Lines 3 to 7 are recorded: for each tool, in how many of its 5 visits an INP report reached the collector.
  • Line 10 prints one sentence per tool. The first is web-vitals: INP arrived in 5 of 5 visits. Sentry, Datadog RUM and Grafana Faro print 5 of 5 too.
  • The last is Elastic APM RUM: INP arrived in 0 of 5 visits.

Four tools got every INP out. Elastic got none. Its one report, with LCP, CLS, FCP and TTFB, had arrived about a second before the visit ended.

Switching tabs told a different story. Here are both endings, for every tool:

What reached the collector, from 5 visits each: web-vitals: Follow a link, All five by +121 ms; Switch tabs, All five by +45 ms. Sentry: Follow a link, All five by +27 ms; Switch tabs, INP in 3 of 5. Datadog RUM: Follow a link, All five by +24 ms; Switch tabs, INP only at close. Grafana Faro: Follow a link, All five by +25 ms; Switch tabs, All five by +31 ms. Elastic APM RUM: Follow a link, INP never arrived; Switch tabs, INP +836 to +1,509 ms.Follow a linkSwitch tabsweb-vitalsAll five by +121 msAll five by +45 msSentryAll five by +27 msINP in 3 of 5Datadog RUMAll five by +24 msINP only at closeGrafana FaroAll five by +25 msAll five by +31 msElastic APM RUMINP never arrivedINP +836 to +1,509 ms
Each card sums up 5 visits. Times are counted from the moment the visit ended. Shaded: INP late, missing, or sent only as the tab closed.
  • Sentry's INP arrived in 3 of 5 visits.
  • Datadog sent one report straight away, with TTFB and CLS. LCP and INP came only as the tab closed, 5 s later. A real visitor could leave a tab open far longer than that.
  • Elastic's INP did arrive this time, in every visit. But it was late.

Why Elastic's INP was late, or lost

Elastic's source code shows what happens. When the page is hidden, Elastic builds its INP report and adds it to a queue. The queue doesn't always go out at once. Elastic's docs describe a timer:

"The agent maintains a single queue to record transaction and error events when they are added. This option sets the flush interval in milliseconds for the queue."

The option is flushInterval, and its default is 500. To flush a queue is to send everything in it. So a report can wait half a second before it leaves.

Real-life analogyCards in a tray, posted when the timer rings

At one café, the manager doesn't post each feedback card as a customer hands it over. She drops it in a tray and sets a kitchen timer. When the timer rings, she posts the whole tray. But if the café shuts for good before it rings, the cards never leave. And on a quiet afternoon, she glances at the timer only once an hour, so the tray goes out late.

In real life: A customer's card goes in the tray
In Elastic's SDK: The INP report goes in the queue as the page hides
In real life: The kitchen timer
In Elastic's SDK: flushInterval: 500 ms by default
In real life: The café shutting before the timer rings
In Elastic's SDK: Following a link: the page is gone before the timer fires
In real life: Glancing at the timer only once an hour
In Elastic's SDK: Chrome checking a hidden page's timers once per second

The recordings fit that timer:

  • Following a link: the old page was gone before the timer could fire. So the report never left.
  • Switching tabs: a hidden tab stays alive, so the timer does fire, but late. Chrome slows down timers in hidden pages. For a page hidden for less than 5 minutes, Chrome's blog says "The browser will check timers in this group once per second".
Elastic's INP, after a tab switchPop out in the code editor (opens in a new tab)JavaScript
// When Elastic's INP report arrived, in ms after the visitor switched tabs, in 5 visits.const arrived = [1172, 1509, 836, 1131, 1479];const flushInterval = 500; // Elastic's default wait, in ms. const sorted = [...arrived].sort((a, b) => a - b);console.log(`from ${sorted[0]} to ${sorted.at(-1)} ms, median ${sorted[2]} ms`);console.log(`the shortest was ${sorted[0] - flushInterval} ms longer than the wait`);
  • Line 2 is recorded: when each INP report arrived, in ms after the switch. Line 3 is Elastic's default wait.
  • Line 5 sorts a copy of the times. Line 6 prints from 836 to 1509 ms, median 1172 ms.
  • Line 7 prints the shortest was 336 ms longer than the wait.

No report beat its 500 ms timer. That fits a timer that's checked only once per second.

Elastic's source also compresses each report with CompressionStream, a browser API that gzips data. Every Elastic report the course caught was gzip-compressed. Only then does it send the report, with fetch() and keepalive. keepalive lets a request outlive its page, but only once the request has started.

Compare web-vitals' six lines. They send in the same moment the page is hidden, with sendBeacon(), so there's no timer to miss. All five metrics arrived from every visit, both ways.

Why it matters: a lost report doesn't look like an error. The dashboard just has fewer INPs, and the ones it has come from the visitors who didn't follow a link. So test any tool the way the course did: follow a link, switch tabs, and count what arrived.

Two tools, two answers

Suppose the team ran Elastic and web-vitals side by side. Would their INP agree? Not here. web-vitals heard every visit's INP, and Elastic heard only the visits that switched tabs. Each tool's p75 comes from the visits it heard about.

Real-life analogyTwo shops' scales

You weigh the same bag of tea at two shops, and the scales disagree. Neither is broken: one weighs the tea with its paper bag, and the other without. So before you compare, you ask each shop what its scale includes. And to see whether your tea is getting lighter, you weigh it on the same scale every week.

In real life: Two shops' scales
In RUM tools: Two RUM tools measuring the same visits
In real life: Weighing with the paper bag, or without it
In RUM tools: Which visits, and which moments, each tool counts
In real life: Each scale's own label
In RUM tools: Each tool's own name for a metric
In real life: Weighing on the same scale every week
In RUM tools: Comparing a tool with its own earlier numbers

Tools even give the same metric different names. The course searched every report for each metric's name. For LCP, the search matched largest_contentful in Datadog's reports, lcp in Faro's, and largestContentful in Elastic's.

Field data from every browser found the same with browser engines: each one is its own thermometer. So ask each vendor how it counts, and compare a tool with its own earlier numbers.

Cost, sampling, and data ownership

A hosted tool is a restaurant: you pay for every plate. So the first question is what counts as a plate. Look for the unit on the pricing page before you look at the price:

  • Datadog's billing docs say "Datadog charges per one thousand (1,000) sessions ingested" into its RUM. A session is one visitor's trip through your site. Datadog says it "usually includes multiple page views".
  • Vercel counts events for Speed Insights, and prices Plus by the event.
  • SpeedCurve sizes its RUM plans in page views. Its guide talks of "a RUM plan that covers a maximum of 10 million page views per month".

Prices change often, so read today's numbers on each vendor's own pricing page.

What a free tier leaves out

Real-life analogyA free sample at the shop

On the shop counter sits a tray of free samples: a small piece of each cake. It tells you how the cake tastes. But it isn't the whole cake, and it doesn't tell you the price. When the tray runs out, there are no more samples until someone refills it.

In real life: A small piece of each cake
In pricing: A free tier that shows part of the data
In real life: The tray running out
In pricing: Reaching the free tier's limit
In real life: No more samples until the tray is refilled
In pricing: Collection pausing for a while
In real life: The price of the whole cake
In pricing: The paid tier's price per event

Vercel's free tier works like that tray. Its docs say:

  • The free tier shows the Real Experience Score only. Each Core Web Vital on its own needs Plus.
  • It includes a fixed number of events over the last 30 days, shared across the team.
  • "When your team reaches the free tier limit, Vercel pauses collection for those projects for at least 14 days."
  • "Your sites keep running, and you do not incur event overage charges for the free tier."

So taste a tool on its free tier, but check what it leaves out. A busy month can use up the free events, and then collection pauses.

Sampling: fewer visits, the same mix

To pay for fewer plates, send fewer visits. That's sampling: keep a random share of visits, and drop the rest. The share you keep is the sample rate. SpeedCurve's guide works out a rate for a plan:

Picking a sample ratePop out in the code editor (opens in a new tab)JavaScript
// SpeedCurve's example: 900 million page views a month, and a plan for 10 million.const pageViews = 900_000_000;const plan = 10_000_000; const rate = Math.floor((plan / pageViews) * 100);console.log(`sample rate: ${rate}%`);console.log(`sampled page views: ${((pageViews * rate) / 100).toLocaleString("en-US")} a month`);
  • Lines 2 and 3 are SpeedCurve's example: 900 million page views a month, and a plan for 10 million.
  • Line 5 works out the rate as a percentage. Math.floor() rounds down, so the sample stays inside the plan.
  • Line 6 prints sample rate: 1%. Line 7 prints sampled page views: 9,000,000 a month.

SpeedCurve's guide gives the same answer: "set the sample rate to 1%." And 9 million fits inside the plan.

Each tool also picks a sample rate for you, until you change it. Like the landlord's furniture, it's worth checking:

  • SpeedCurve's guide: "The default sample rate is 100%."
  • Datadog's sessionSampleRate "controls the percentage of overall sessions being tracked. It defaults to 100%, so every session is tracked by default."
  • Sentry is the other way round. Sentry calls the timed work it records, like a page load, a transaction. Its docs say that until you set a rate, "no transactions will be sent to Sentry." The course set tracesSampleRate to 1, to send them all.

Does sampling bend the numbers? Not if it's random. Sentry says a uniform rate "Provides an even cross-section of transactions, no matter where in your app or under what circumstances they occur." The mix stays the same. There are just fewer visits in each group.

Who keeps the data

Last: where do the reports end up, and who can read them? With your own pipeline, or an open-source tool you run, they stay on your servers. With a hosted tool, the vendor keeps them.

Some tools let you send reports through your own server first. Here is the Datadog setup the course recorded:

Datadog, through your own serverJavaScript
import { datadogRum } from "@datadog/browser-rum";datadogRum.init({  applicationId: "00000000-0000-0000-0000-000000000000", clientToken: "pub00000000000000000000000000000000", site: "datadoghq.com",  service: "shop", proxy: COLLECTOR + "/datadog", sessionSampleRate: 100, sessionReplaySampleRate: 0, trackUserInteractions: true,});
  • Line 1 loads Datadog's SDK. Lines 2 to 5 start it.
  • Line 3 holds the account details. The course used made-up ones, because its own server played the collector.
  • Line 4 sets proxy to an address of your own, so every report goes there first. sessionSampleRate: 100 keeps every session, which is the default anyway.

A proxy is a server that passes requests on. Datadog's guide says where they go next: "The RUM data still needs to be forwarded to Datadog from the proxy." It even asks the proxy to add each visitor's IP address, in an X-Forwarded-For header, "for accurate geoIP": working out roughly where the visitor is. So a proxy changes the route, not the owner, like handing your letter to a neighbour to post.

Then there's what a tool learns about your visitors. Datadog's docs say it "uses cookies to stitch together the various steps of your users into a session." Cloudflare's say "Cloudflare Web Analytics does not collect or use your visitors' personal data." Where consent is needed, Sending performance data showed how to wait for it.

Here is everything to ask, in one place. Read each row across: the question, then how to answer it for any tool.

Questions to ask before you pick a RUM tool
AskHow to find out
What do I need to see?Write your questions down first. Is page speed enough, or do you need errors and server traces next to it?
What does it add to every page?Build its smallest setup, minify it and compress it. Then time its script with a mark on each side.
Does its last report arrive?Follow a link, then switch tabs, and count what reached the collector each time.
What is billed?Find the unit on its pricing page: sessions, page views or events. Then read today's price.
What does it do until I change it?Read its default sample rate, and what a free tier leaves out.
Who keeps the data, and can I get it out?Read where reports are stored, and whether you can export the raw data.
What does it collect about the visitor?Read its privacy docs, including cookies and IP addresses. Turn off what you don't need.

Which kind of tool is it?

Ask what each tool is mainly for. Is it built only for speed, part of a bigger platform, your host's own, or code you run yourself?

Which kind of tool is it?
  • SpeedCurve RUM, with its lux.js snippet
  • Akamai mPulse, built on boomerang
  • Sentry, which you may already use for errors
  • Datadog RUM, next to your logs and server traces
  • Speed Insights, turned on in your Vercel project
  • Cloudflare Web Analytics, free with an account
  • Grafana Faro, sending to an Alloy you run
  • web-vitals, beaconing to your own endpoint
Try it yourself
0 of 8 correct

Sort each tool by what it's built for.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "A RUM SDK is tiny." No. Datadog's smallest setup was 73,923 bytes with brotli, 23.5 times web-vitals.
  • "An async script costs nothing." No. It doesn't stop the HTML while it downloads. But it still downloads, and it still runs on the main thread.
  • "Every tool gets INP out when the visitor leaves." No. Elastic's INP never arrived from the visits that followed a link.
  • "Two tools' numbers should match." No. They can count different visits at different moments, and even name metrics differently. Compare a tool with itself.
  • "Sampling makes the numbers wrong." No. A random sample keeps the same mix of visits, just fewer of them.
  • "A proxy means I own the data." No. Datadog's proxy still forwards it to Datadog.
  • "Open source means free." No. The code costs nothing to use, but you run the servers, and you pay for them.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upA bigger plan

The shop now buys a plan for 50 million page views. What does the first line print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// SpeedCurve's example: 900 million page views a month, and a plan for 50 million.
const pageViews = 900_000_000;
const plan = 50_000_000;

const rate = Math.floor((plan / pageViews) * 100);
console.log(`sample rate: ${rate}%`);
console.log(`sampled page views: ${((pageViews * rate) / 100).toLocaleString("en-US")} a month`);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upSwitching tabs

    These are the visits that switched tabs instead. What does the Datadog line print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // In how many of 5 visits that switched tabs did each tool's INP arrive before the tab closed?
    const inpArrived = {
      "web-vitals": 5,
      Sentry: 3,
      "Datadog RUM": 0,
      "Grafana Faro": 5,
      "Elastic APM RUM": 5,
    };
    
    for (const [tool, count] of Object.entries(inpArrived)) console.log(`${tool}: INP arrived in ${count} of 5 visits.`);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeSentry's missing INPs

      Sentry's INP arrived in 3 of the 5 visits that switched tabs. In which two visits did it never arrive?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFaro's first report

        In Faro's first visit that followed a link, how many ms before the visit ended did its first report arrive?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeWhen a Datadog session ends

          Datadog's billing docs say a session expires after how many minutes of inactivity?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeVercel's free tier

            How many Speed Insights events does Vercel's free tier include over the last 30 days?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask what the tool sends and when, what you pay for, and where the data ends up.

              Choosing a RUM tool quiz · 8 questionsScore: first tries count
              1. Question 1 of 8In the visits that switched tabs, what does the last line print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // In how many of 5 visits that switched tabs did each tool's INP arrive before the tab closed?
                const inpArrived = {
                  "web-vitals": 5,
                  Sentry: 3,
                  "Datadog RUM": 0,
                  "Grafana Faro": 5,
                  "Elastic APM RUM": 5,
                };
                
                for (const [tool, count] of Object.entries(inpArrived)) console.log(`${tool}: INP arrived in ${count} of 5 visits.`);

                Choose an answer to see the explanation.

              2. Question 2 of 8What does the second line of Sentry's size example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // Each tool's smallest setup, bundled, minified and brotli-compressed, in bytes.
                const webVitals = 3141;
                const sentry = 49114;
                
                console.log(`Sentry adds ${sentry.toLocaleString("en-US")} compressed bytes to the page.`);
                console.log(`That's ${(sentry / webVitals).toFixed(1)} times as much as web-vitals.`);

                Choose an answer to see the explanation.

              3. Question 3 of 8An SDK puts its last report in a queue as the page hides, and sends the queue when a timer fires. What can happen when the visitor follows a link?

                Choose an answer to see the explanation.

              4. Question 4 of 8Two RUM tools on the same site show different p75 INPs. What's the best next step?

                Choose an answer to see the explanation.

              5. Question 5 of 8You set a 10% sample rate. What changes?

                Choose an answer to see the explanation.

              6. Question 6 of 8You send Datadog's reports through a proxy on your own server. Where do they end up?

                Choose an answer to see the explanation.

              7. Question 7 of 8A team on Vercel's free Speed Insights tier reaches its event limit. What happens?

                Choose an answer to see the explanation.

              8. Question 8 of 8Which way of getting RUM data leaves your team running the collector, the storage and the dashboards?

                Choose an answer to see the explanation.

              Key takeaways

              • Every RUM tool measures, sends, stores and shows. Tools differ in who runs each job.
              • Four kinds: speed specialists, observability platforms, hosting-provider analytics, and open-source tools you run.
              • Weigh the SDK and time its script. Here, the smallest setups ran from 3,141 to 73,923 bytes with brotli.
              • Follow a link and switch tabs, then count what arrived. A report sent on a timer after the page hides can be late, or lost.
              • Two tools can disagree on the same visits. Compare a tool with its own earlier numbers.
              • Read the unit before the price, check the defaults, and see what a free tier leaves out. A random sample keeps the same mix.
              • Know where the data lives. A proxy changes the route, not the owner.

              Remember the one-liner.
              Pick by what you need, then test that the last report arrives.

              Sources

              The facts on this page come from these docs and source files:

              Coming next: From field data to a fix. Your tool shows a slow group of visitors. Next, you'll use attribution to find the cause, reproduce it in the lab, fix it, and confirm the fix in the field.

              CompleteFrontend Clear concepts. Working examples.