6 frontend mistakes that tank your Lighthouse score in 2026
Total Blocking Time is 30% of the Lighthouse score, yet most teams fix the wrong metric. The six production mistakes that crater it, and how to fix each.
A Lighthouse performance score of 98 on your laptop and 41 on a real phone is not a measurement error. It is the distance between a lab test and the field, and most teams never see the second number until a client forwards a PageSpeed report. Lighthouse is a synthetic audit: it loads your page once, on a simulated mid-tier device and a throttled network, and grades the result. Google ranks on something else, the Core Web Vitals collected from real Chrome users. When the two disagree, production is the one your users feel.
These are the six mistakes we see most often when a Next.js or React app scores well in development and badly in production. Each one maps to a specific metric and a specific weight, so fixing the right one first is most of the work.
How Lighthouse actually scores performance
The performance score is a weighted average of five lab metrics. In Lighthouse 12, the current model assigns:
- Total Blocking Time (TBT): 30%, the single heaviest metric
- Largest Contentful Paint (LCP): 25%
- Cumulative Layout Shift (CLS): 25%
- First Contentful Paint (FCP): 10%
- Speed Index: 10%
TBT and LCP together are 55% of the number. Improving a 30%-weighted metric moves the score three times more than improving a 10%-weighted one, which is why "we shaved 200ms off First Contentful Paint" often does nothing visible to the grade. One metric is missing from that list on purpose. Interaction to Next Paint (INP) replaced First Input Delay as a Core Web Vital on 12 March 2024, but INP is a field metric. It does not appear in the Lighthouse lab score at all. You can score 100 in the lab and still fail INP for every real user.
Mistake 1: Reading the lab score as if it were the field score
The green number in your terminal is a single synthetic run. Google's ranking signal, and the assessment in Search Console, come from the Chrome User Experience Report, aggregated at the 75th percentile of real visits. A page passes Core Web Vitals only when 75% of visits hit LCP under 2.5s, INP under 200ms, and CLS under 0.1. A lab score of 95 with failing field data is common, and the field is what ranks. Treat the lab score as a debugging tool that tells you where to look, not as the result. The result lives in the field data.
Mistake 2: A render-blocking head
Every synchronous <script> and every stylesheet in the document head blocks the browser from painting. Third-party tags are the usual culprit: analytics, consent banners, A/B testing tools, chat widgets, tag managers, most of which default to synchronous loading. They delay FCP and LCP, and their main-thread work inflates TBT. The fix is mechanical. Defer or async every non-critical script, load third-party tags after interaction or off the main thread, and inline only the critical CSS the first screen needs. Render-blocking requests are one of the most common reasons a page fails LCP even after the images are optimized.
Mistake 3: Lazy-loading the LCP image
The single most common LCP regression we find is loading="lazy" on the hero image. Lazy-loading is correct for everything below the fold and wrong for the one element that defines LCP, because the browser delays the request that the metric is timing. Frameworks that lazy-load by default hide this behind a component, so it is easy to ship without noticing. Mark the LCP image eager, add fetchpriority="high", and preload it. Then confirm it is actually optimized: a 2MB hero served as a PNG will fail LCP on a throttled connection no matter how you prioritize the request.
Mistake 4: Hydrating the whole page
JavaScript is the metric killer. Every kilobyte you ship is parsed, compiled, and executed on the main thread, and on a mid-tier phone that thread runs roughly four times slower than your laptop. Shipping a fully client-rendered page, or hydrating static content that never needed interactivity, drives TBT (30% of the score) straight up and degrades field INP on every tap. This is where Server Components, partial hydration, and a ruthless bundle audit pay for themselves. We covered the heavier libraries in the immersive web stack: most of them do not belong on a marketing page.
Mistake 5: Layout that shifts under the user
CLS is 25% of the score and the easiest to miss in development, because layout shifts only appear when assets load slowly. Three offenders repeat: images and video without explicit width and height (or an aspect-ratio box), web fonts that swap and reflow the text, and content injected above existing content such as banners, ads, and late embeds. Reserve space for everything that loads late. Set font-display deliberately and preload the font files. An empty container with fixed dimensions beats a layout that jumps after first paint.
Mistake 6: Testing on your own machine
Lighthouse mobile simulates a mid-tier device on a throttled connection. Your development machine is an M-series laptop on office fibre. The two can differ by 50 points or more, and the field sits closer to the throttled run. Auditing in your everyday browser, signed in, with extensions active, makes it worse: extensions inject scripts that count against the score. Test in an incognito window at minimum, or run Lighthouse in CI on a fixed device profile so the number is comparable between commits instead of between machines.
How to stop the regressions from coming back
One-off fixes decay. A page that scores 95 today drifts back into the red as features ship, scripts get added, and images grow. The durable fix is a budget enforced in CI. Run Lighthouse CI on every pull request with a performance budget, a minimum score or hard caps on TBT and LCP, and fail the build when a change crosses it. Pair that lab gate with field monitoring from the Chrome User Experience Report or your own real-user data: the lab catches regressions before release, the field tells you what users actually get. For the procedural version of shipping green from the start, see how we ship Core Web Vitals in green on Next.js.
Sources
Frequently asked questions
- Why is my Lighthouse score different every time I run it?
- Lighthouse runs a single synthetic load, so the result varies with CPU and network contention on the machine running it. A background process, an open tab, or a busy connection can move the score by 10 to 20 points between runs. Run the audit several times and use the median, or run it in CI on a fixed device and network profile so the variance disappears and the number is comparable between commits.
- Does a 100 Lighthouse score mean my site passes Core Web Vitals?
- No. Lighthouse is a lab tool that grades one synthetic run, while Core Web Vitals are assessed from real users in the field at the 75th percentile. INP, one of the three Core Web Vitals, is not even part of the Lighthouse performance score. A perfect lab score with slow real-user data still fails the assessment Google uses for ranking, so you need both lab audits and field monitoring.
- Which metric should I fix first to raise my score?
- Start with Total Blocking Time, because it carries 30% of the weight and almost always traces back to too much JavaScript on the main thread. Cut the bundle, move work to Server Components, and defer third-party scripts. Then look at Largest Contentful Paint at 25%, which is usually the hero image: prioritize and optimize it. Fixing the two heaviest metrics moves the score far more than chasing the 10% ones.
- Is the Lighthouse score itself a Google ranking factor?
- The lab score is not. Google ranks on Core Web Vitals measured from real users, not on the synthetic Lighthouse number. The lab score is a useful proxy for finding problems before release, but a page can rank well with a mediocre lab score if its field data is good, and a page with a perfect lab score can rank poorly if real users get a slow experience. Optimize for the field, use the lab to debug.
Studio
Start a project.
One partner for the digital product you need to build. Faster delivery, modern tech, lower costs. One team, one invoice.