Skip to content
CoreXAlpha
Performance

We scored 100 on Lighthouse and the site still felt slow

Lab measurement loads a page once in a controlled environment. Real users do not behave that way. What we do when field data contradicts the lab score.

Selin KorkmazFrontend Architect3 min read

A client's new site scored 100 across all four Lighthouse categories. That same week, the most common sentence in user feedback was: "It freezes while loading."

Both were true.

What lab measurement does not measure

Lighthouse performs a single load in a controlled environment, with a cold cache and a fixed network profile. That is an excellent basis for comparison. But it does not represent real user experience in three respects:

  • Device diversity. The lab profile emulates a mid-range device. Part of your real traffic comes from much slower ones.
  • Interaction timing. Users do not wait for the page to load; they click while it loads. Lighthouse does not click.
  • Navigation type. A cold load and an in-app navigation have different costs; the lab measures only the first.

In our case the problem was exactly the second: INP (Interaction to Next Paint) was invisible in the lab measurement.

What we collect in the field

On every project we enable real user monitoring (RUM) with the first release. The field set is deliberately small:

| Field | Why | | --- | --- | | LCP, CLS, INP, TTFB | Core metrics | | Route template | Which page type is problematic | | Device class | Low / mid / high | | Connection type | 4g, 3g, slow-2g | | Navigation type | Cold load or client-side navigation |

We collect no personal data; URLs are reduced to templates (/blog/[slug]), keeping the measurement anonymous.

When deciding, we look at the 75th percentile, not the average. Averages hide the worst experiences.

The three problems we found

1. Long tasks were swallowing the first click.

The page looked ready but the main thread was still busy hydrating. The user clicks, nothing happens, they click again.

The fix was not to split the work but to remove it. We converted components that needed no interactivity into server components; the count of components in the client bundle fell from 34 to 11. INP at p75 dropped from 310 ms to 140 ms.

2. The font arrived late, but CLS was zero.

Because CLS was zero, nobody had looked. Yet the text was invisible until the font loaded (font-display: block behaviour). Users were staring at empty space.

With display: swap and a properly scaled fallback font, the text appeared immediately — and no shift occurred, because the fallback's metrics were tuned close to the target font.

3. A third-party script was delaying first paint.

The analytics script was loading synchronously in <head>. In the lab it came from cache so its cost was invisible; in the field, on slow connections, it delayed LCP by 900 ms.

We moved it to next/script with the afterInteractive strategy. The only thing we lost was measurement of users who bounce immediately — an acceptable trade.

Result

Four weeks of field data after the three changes:

  • LCP (p75): 2.9 s → 1.6 s
  • INP (p75): 310 ms → 140 ms
  • CLS (p75): 0.02 → 0.01

The Lighthouse score did not change. It was already 100.

The lesson

A lab score is a regression test: it cheaply answers "is the new release worse than the old one?" It does not answer "what are my users experiencing?"

Keep both measurements. When they disagree, believe the field.

  • Core Web Vitals
  • Performans
  • RUM
  • Next.js
All articles

Related articles

Let’s build the future together

Whatever your idea, we have the technology and the team to make it real. Let’s start with a free one-hour discovery call.