Skip to main content

From field data to traces: audit web performance

An audit that segments field Core Web Vitals, reproduces a user journey and verifies where browser or server time is spent.

Testable hypotheses, a prioritised backlog and a repeatable diagnostic method.

SpeedCurve dashboard showing web performance metrics

Technical guide · Web performance

By Byrds ConsultingUpdated on 5 min read

A Lighthouse score is a clue, not an explanation for what real users experience. Acting on it means connecting field signals to browser work and, when the instrumentation supports it, the server path.

Starting point

Fast in the office, inconsistent in the field

In this scenario, a Next.js application serves public pages and a signed-in product area. Local tests look healthy, yet support reports slow interactions on mobile and Core Web Vitals vary widely between routes.

The team has already applied the obvious optimisations. Another checklist will not reveal where time is actually being lost, so the audit starts with an observed user experience and works back to a cause the team can verify.

The brief

Give the team a shared language and a diagnostic method they can reuse.

  • Segment field data by route, build, viewport class and navigation type.
  • Relate a slow interaction to browser work and, when an ID is propagated, the corresponding server trace.
  • Fix one demonstrated bottleneck before adding more dashboards.

Investigation

Start with one user experience, not a site-wide average

We choose an affected journey: open an item, change a filter, then render the results. Field data shows which population experiences the slowdown; a controlled test then replays the sequence with representative network and CPU constraints.

Poor INP may come from an expensive React render, a long task or a third-party script. A poor LCP may be caused by server response time, late resource discovery or image decoding. The metric narrows the investigation, but it does not identify the culprit on its own.

Affected population

Is the regression limited to one route, viewport class, region or build?

User moment

Which element becomes the LCP, and which interaction produces the observed INP?

Browser work

Is the main thread running JavaScript, rendering React, calculating layout or executing third-party code?

Server path

For an instrumented request, is time spent in Next.js, the API, the database or an external service?

The investigation ends with a testable hypothesis for a specific population and action. A targeted fix does not claim to speed up every page.

Measurement design

Preserve context without inventing correlation

A field measurement becomes actionable when it includes a normalised route, build, viewport class, navigation type, page ID and timestamps. Those dimensions identify the relevant segment without collecting unnecessary personal data.

They do not prove that a Web Vital corresponds to a particular trace span. Exact correlation requires an ID created on the server, exposed to the browser and propagated through the relevant requests. Without that mechanism, Next.js and OpenTelemetry traces can still be compared for the same route, build and time window, but the analysis must be described as a comparison rather than a one-to-one link.

Field measurement

LCP, INP and CLS with route, build, viewport and navigation context.

Browser diagnosis

Long tasks, React renders, layout and third-party scripts explain client-side work.

Server traces

Rendering, data-access and dependency spans are filtered to the same route, build and time window.

Testable hypothesis

One suspected cause, one change and the signal expected to move.

Shared dimensions narrow the search; only a propagated ID establishes a direct link between a navigation and a trace.
Send Web Vitals with actionable contextTypeScript
import { onCLS, onINP, onLCP, type Metric } from 'web-vitals'

let registered = false

type ViewportClass = 'mobile' | 'desktop'

function getViewportClass(): ViewportClass {
    return matchMedia('(max-width: 767px)').matches
        ? 'mobile'
        : 'desktop'
}

export function registerWebVitals(normalizedRoute: string) {
    if (registered) return
    registered = true

    const pageId = crypto.randomUUID()
    const report = (metric: Metric) => {
        const payload = {
            route: normalizedRoute,
            build: process.env.NEXT_PUBLIC_BUILD_ID ?? 'dev',
            viewport: getViewportClass(),
            navigation: metric.navigationType,
            pageId,
            pageStartedAt: performance.timeOrigin,
            reportedAt: new Date().toISOString(),
            metric: {
                id: metric.id,
                name: metric.name,
                value: metric.value,
                rating: metric.rating,
            },
        }

        const body = new Blob([JSON.stringify(payload)], {
            type: 'application/json',
        })
        navigator.sendBeacon('/api/vitals', body)
    }

    onCLS(report)
    onINP(report)
    onLCP(report)
}

Register this callback once per document load; this snippet does not report each App Router soft navigation as a separate measurement. The application must inject the build ID at deployment. A sound diagnosis depends on consistent dimensions and an honest statement of confidence.

Delivery

Five steps from signal to verified fix

The audit moves one journey at a time. Each step produces something the team can inspect or measure: a segment, trace capture, hypothesis, diff or production result.

  1. 01

    Validate the field signal

    Validate collection, split the data by route and isolate the affected population.
  2. 02

    Reproduce the journey

    Use a stable scenario, a credible device profile and the same build.
  3. 03

    Find where the time is going

    Combine a flame chart, React Profiler and server spans when the request is instrumented.
  4. 04

    Address one cause

    Shorten a long task, move a calculation, remove a request waterfall or prioritise a resource.
  5. 05

    Measure after release

    Compare the same segment on the new build and keep a regression check in place.

If the expected signal does not move, the hypothesis is rejected and documented. That prevents the team from repeating an appealing but ineffective optimisation.

Qualitative outcomes

Performance discussions grounded in journeys, not impressions

Product, design and engineering use the same evidence. An alert names the affected population and action; a PR states the expected signal. Support can pass along the route, build and viewport context without asking the user to open DevTools.

What the team can verify

A shared diagnostic method that remains useful after each release.

  • A field regression can be tied to a route, build and user segment.
  • For instrumented requests, traces separate rendering time from dependency time when those dependencies emit their own spans.
  • Every material optimisation starts with a falsifiable hypothesis.
  • Unused dashboards can be removed without losing the diagnostic method.

Key takeaway

Core Web Vitals describe an experience, not the line of code at fault. The audit preserves enough context to propose a cause, test it and measure the outcome.

Sources and documentation

  1. Web Vitals. web.dev. The official definition of Core Web Vitals and field measurement.
  2. OpenTelemetry with Next.js. Next.js Documentation. Official guidance on tracing and custom spans in Next.js.
  3. Performance measurement APIs. Node.js Documentation. Official reference for Node.js runtime measurement APIs.

Let’s check whether this format fits your context

A 30-minute conversation is enough to qualify scope, risks and the most useful next step.

Schedule the call