Vercel Logo

Observability Sampling

Speed Insights captures performance data from every page view. Web Analytics counts every visit. Drains forward every log line. The defaults are 100% sampling because the tools want to be useful out of the box. The defaults are also the most expensive way to run them.

You don't need a full sample to know if your Largest Contentful Paint is regressing. A 25% sample is enough to spot the trend, and it costs you a quarter as much. If you do have specific routes that don't deserve any tracking, the beforeSend callback in Web Analytics is the place to drop them.

For Saturday, we'll wire up both tools with sensible defaults. The shop doesn't have an admin section yet, but the filter pattern is what you'll keep coming back to as the app grows.

What are Web Analytics and Speed Insights?

Two opt-in measurement products. Web Analytics counts visitors and page views. Speed Insights collects Core Web Vitals from real sessions. Both are billed by volume, events for one and data points for the other, which is why sampling is the cost lever.

Outcome

<Analytics> and <SpeedInsights> are mounted in the root layout. Speed Insights uses a 25% sample rate. Analytics filters out any future /admin routes before sending events.

Hands-on exercise 4.4

One file. The dependencies already exist; you're wiring the components.

Requirements:

  1. Open app/layout.tsx
  2. Import Analytics from @vercel/analytics/next
  3. Import SpeedInsights from @vercel/speed-insights/next
  4. Mount both at the end of <body>, after the footer
  5. On <SpeedInsights>, set sampleRate={0.25}
  6. On <Analytics>, pass a beforeSend function that returns null for any event whose url includes /admin (we don't have admin routes yet; this is preventive)
  7. Commit, push, redeploy

Implementation hints:

  • The components are inert during local development. They only send data when running on a Vercel-hosted deployment. So you won't see the integration "work" until after deploy.
  • sampleRate is a number between 0 and 1. 0.25 means roughly one in four sessions get measured. That's enough granularity to surface real regressions but small enough to keep observability costs predictable.
  • beforeSend runs per event, client-side. Returning null drops the event entirely. Returning the event lets it through. You can also mutate the event and return a modified version, but for filtering, null is the answer.
  • The url field on the event is the page URL. If your filter is path-based, you can check event.url.includes('/admin') or use new URL(event.url).pathname.startsWith('/admin') for stricter matching.

Try It

After the redeploy, open Saturday in a browser and navigate around. Then check the two dashboards.

For Speed Insights, open Saturday > Speed Insights. Within a few minutes you should see data appear. The sample rate means it'll take more total traffic to fill out the graphs than at 100% sampling, but the metrics shown will be representative.

Real Experience Score   84
Sampled visits today    12 (sampled from ~48 total)
LCP                     1.8s
FID                     12ms
CLS                     0.04

For Web Analytics, open Saturday > Analytics. You should see your visits counted in real time. The pages list should show /, /drops, /products/foundry, etc., but no /admin paths, even if you visit one in a browser. (Try https://YOUR_DOMAIN/admin/test. Next.js will 404, but more importantly, Analytics won't record the visit.)

To verify the filter is actually firing, open DevTools > Network and look for requests to va.vercel-scripts.com. On regular pages you'll see the analytics beacons going out. On /admin/* paths, no beacons.

Done-When

  • app/layout.tsx imports and renders <Analytics> and <SpeedInsights>
  • <SpeedInsights> has sampleRate={0.25}
  • <Analytics> has a beforeSend that drops /admin events
  • Speed Insights dashboard shows sampled data within a few minutes of deployment
  • Analytics dashboard shows visits but excludes any /admin paths

Troubleshooting

Speed Insights shows zero visits.

A few options. Sampling at 25% means low-traffic sites can take time to register their first sampled session, keep refreshing. Also confirm the SpeedInsights component is actually rendering: view the page source on production and look for the SpeedInsights script tag. If it's missing, the import path is wrong or the deploy didn't pick up the change.

Analytics is counting /admin pages even though I set the filter.

Two common issues. The beforeSend function must return the event (or a modified copy) for it to pass through; if you forget the return statement, all events get dropped silently. Also check that you're testing against a real Vercel deployment, not local dev, the integration is a no-op locally.

Why not sample Analytics too?

You could, but Web Analytics is typically priced per event with a free tier per project, so for most teams the volume isn't the cost driver, keeping the data clean is. Speed Insights, on the other hand, is priced per data point and tends to fill faster, so sampling is the higher-leverage knob.

Solution

The finished app/layout.tsx is on the complete branch if you want to check your version against it.

app/layout.tsx
import type { Metadata } from "next";
import Link from "next/link";
import { Analytics } from "@vercel/analytics/next";
import { SpeedInsights } from "@vercel/speed-insights/next";
import "./globals.css";
 
// ...metadata unchanged
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className="min-h-screen flex flex-col">
        {/* ...header, main, footer unchanged */}
 
        <Analytics
          beforeSend={(event) => {
            if (event.url.includes("/admin")) return null;
            return event;
          }}
        />
        <SpeedInsights sampleRate={0.25} />
      </body>
    </html>
  );
}

A note on Vercel Drains for log shipping: if you're forwarding production logs to Datadog, Splunk, S3, or a custom HTTP endpoint, the Drains UI has a sampling field too. The default is 100%. The same logic applies, sample at 25-50% for general visibility, sample at 100% for specific log levels you must not lose (errors, security events). Saturday doesn't ship logs externally yet, so we'll leave that for the day it does.

That's the last hands-on cost setting in the course. One more lesson in this section ties the whole cost arc together: instead of hunting these levers by hand, you'll point an agent skill at a real project and let it find them from production metrics.

Was this helpful?

supported.