Skip to main content
DISPATCH // CORE WEB VITALS

How to Improve Lighthouse Score: Real-World Engineering Guide

A practical, developer-led guide to diagnosing bottlenecks, optimizing Core Web Vitals, and improving your Lighthouse score without breaking your site.

ESTIMATED EFFORT 12 min read
VM

VISHAL MEHTA

Founder & Principal Architect, HWT TECHY

How to Improve Lighthouse Score: Real-World Engineering Guide
GOOGLE STORIES HUB

Explore our full library of interactive 9:16 visual engineering and SEO stories on Google Discover.

Explore Stories ⚡
Share Article
Top Summary Answer KEY TAKEAWAYS

Learn how to improve your Lighthouse score with real-world engineering tactics. Fix LCP, TBT, and CLS using concrete code and diagnostic workflows.

How to Improve Lighthouse Score: Real-World Engineering Guide

Many engineering teams and marketing managers are familiar with the anxiety of running a Google Lighthouse test. You click "Analyze," wait for the progress bar to finish, and watch a red or orange score of 45 pop up on your screen.

Often, the immediate reaction is to install a quick-fix caching plugin or run some generic image compression. But these superficial patches rarely move the needle in a meaningful way.

Lighthouse simulates a mid-tier mobile device on a throttled slow-4G connection. To achieve a high score, your site must be built with structural efficiency. This guide bypasses the generic advice and outlines the exact engineering steps required for true page speed optimization.


Table of Contents

  1. Understanding the Lighthouse Scoring Engine
  2. The Diagnostic Workflow: Getting Clean Data
  3. Tackling Largest Contentful Paint (LCP)
  4. Minimizing Total Blocking Time (TBT) and Interaction to Next Paint (INP)
  5. Eliminating Cumulative Layout Shift (CLS)
  6. The Third-Party Script Strategy
  7. Comparison: Optimization Techniques and Business Realities
  8. Frequently Asked Questions
  9. Pragmatic Next Steps

Understanding the Lighthouse Scoring Engine

Before writing any code, you must understand how Lighthouse calculates its performance score. In Lighthouse v10 and v11, the performance score is a weighted average of five distinct metrics:

  • Largest Contentful Paint (LCP): 25% weight. Measures how quickly the main content of a page loads.
  • Total Blocking Time (TBT): 30% weight. Measures the total amount of time between First Contentful Paint (FCP) and Time to Interactive (TTI) where the main thread was blocked by tasks taking longer than 50ms.
  • Cumulative Layout Shift (CLS): 25% weight. Measures visual stability by tracking unexpected layout shifts.
  • First Contentful Paint (FCP): 10% weight. Measures the time from when the page starts loading to when any part of the page's content is rendered on the screen.
  • Speed Index (SI): 10% weight. Measures how quickly contents are visually displayed during page load.

Notice that TBT, LCP, and CLS make up 80% of your total score. If you focus your engineering efforts on these three metrics, your overall score will naturally rise.

It is also critical to distinguish between Lab Data (which Lighthouse generates in a synthetic, simulated environment) and Field Data (real-user metrics collected via the Chrome User Experience Report, or CrUX). While Lighthouse is an excellent diagnostic tool, search engines use real-user Field Data as a ranking signal. Improving your lab score is helpful, but the goal must always be real-world speed.

To see how your website currently performs under search engine criteria, you can run a quick check with our free SEO audit tool to identify immediate rendering and indexing bottlenecks.


The Diagnostic Workflow: Getting Clean Data

Running Lighthouse inside a standard Chrome window with active extensions, dynamic background processes, and a warm browser cache will produce highly inaccurate results. Extensions like ad blockers, password managers, and developer utilities consume CPU cycles on the main thread, artificially inflating your Total Blocking Time.

To get clean, reproducible diagnostic data, follow this workflow:

1. Use the Command Line or PageSpeed Insights

For the most objective results, run Lighthouse via PageSpeed Insights (which runs on clean Google servers) or use the Lighthouse CLI. To run the CLI with throttling configured to match the mobile standard, use the following terminal command:

npx lighthouse https://example.com --view --chrome-flags="--headless" --preset=experimental-mobile

2. Isolate the Browser Environment

If you must run the test locally inside Chrome DevTools, open a clean Incognito window. Ensure all extensions are disabled for incognito mode. Set your DevTools performance throttling manually to 4x CPU slowdown and Fast 3G to simulate real-world mobile conditions.

3. Run Multiple Tests

Network variance is real. Run the test three to five times and take the median value. This ensures that a single slow server response or a temporary network drop does not skew your entire optimization strategy.


Tackling Largest Contentful Paint (LCP)

Largest Contentful Paint is usually determined by the largest image, video block, or text block visible within the viewport. In most web applications, this is the hero image or the primary product image on an eCommerce website development template.

To optimize LCP, you must optimize the critical path of that specific asset. Here is how to do it step-by-step.

1. Eliminate Client-Side Rendering Delays

If your website relies on a client-side JavaScript framework (like standard React or Vue) to fetch data and render the hero image, your LCP will be slow. The browser has to download the HTML, download the JavaScript bundle, parse and execute the JS, fetch the image URL from an API, and finally download the image.

This is why framework selection matters. Moving from a pure client-side architecture to a Server-Side Rendered (SSR) or statically generated framework dramatically improves LCP. For instance, analyzing the SvelteKit vs React paradigm reveals that SvelteKit's native server-side rendering delivers pre-rendered HTML to the browser instantly, allowing the image to start loading before any JavaScript even executes.

2. Use Preload and Fetchpriority

Tell the browser to prioritize the hero image before it parses the rest of the document. You can do this by adding a preload tag to your HTML <head> and using the fetchpriority="high" attribute on your image element.

<!-- Inside your HTML <head> -->
<link rel="preload" fetchpriority="high" as="image" href="/images/hero-banner.webp" type="image/webp">
<!-- Inside your HTML <body> -->
<img 
  src="/images/hero-banner.webp" 
  fetchpriority="high" 
  alt="Our main product display" 
  width="1200" 
  height="630" 
  decoding="async"
>

3. Avoid Lazy Loading the Hero Image

Lazy loading is highly effective for off-screen images, but applying it to your LCP element is a critical mistake. If you use loading="lazy" on your hero image, the browser will delay downloading it until the layout engine determines where the image sits on the page. Ensure that your first 2-3 images are loaded eagerly, while all off-screen images use loading="lazy".


Minimizing Total Blocking Time (TBT) and Interaction to Next Paint (INP)

Total Blocking Time is heavily tied to JavaScript execution. When the browser's main thread is busy executing JavaScript, it cannot respond to user interactions like clicks, scrolls, or keypresses. This directly impacts both your lab TBT score and your real-world Interaction to Next Paint (INP) metric.

1. Break Up Long Tasks

Any JavaScript task that takes longer than 50ms is considered a "long task." If you have a function that processes a massive array of data, it will lock up the main thread. To prevent this, you can yield control back to the browser periodically using a helper function based on setTimeout or requestIdleCallback.

Here is a practical pattern to break up heavy execution loops:

// A utility function to yield execution back to the main thread
function yieldToMain() { 
  return new Promise(resolve => setTimeout(resolve, 0));
}

async function processLargeDataset(items) {
  for (let i = 0; i < items.length; i++) {
    processItem(items[i]);
    
    // Yield control back to the browser every 50 items
    if (i % 50 === 0) {
      await yieldToMain();
    }
  }
}

2. Code-Splitting and Dynamic Imports

Do not force users to download your entire application's JavaScript on the homepage. Use dynamic imports to split your bundles so that heavy components (like interactive charts, maps, or complex forms) are only loaded when needed.

// Instead of importing at the top of the file
// import { HeavyChart } from './components/HeavyChart';

// Load the component dynamically when the user clicks a button
document.getElementById('show-chart-btn').addEventListener('click', async () => {
  const { HeavyChart } = await import('./components/HeavyChart.js');
  const chart = new HeavyChart();
  chart.render();
});

3. Audit Your Dependencies

Many projects drag in massive npm packages for simple tasks. If you only need a simple date formatting utility, do not import the entire Moment.js library. Use lightweight alternatives like date-fns or native browser APIs like Intl.DateTimeFormat.


Eliminating Cumulative Layout Shift (CLS)

Cumulative Layout Shift measures the visual instability of a page. If a user is reading an article or trying to click a button, and the layout suddenly jumps because an image or an ad loaded late, it creates a frustrating user experience.

1. Always Specify Image and Video Dimensions

Always include width and height attributes on your images. This allows the browser to calculate the aspect ratio and reserve the correct amount of space in the layout before the image file actually downloads.

<!-- Correct approach -->
<img src="/images/product-shot.jpg" width="800" height="600" alt="Product detail">

In your CSS, you can make the image responsive while maintaining that aspect ratio:

img {
  max-width: 100%;
  height: auto;
  aspect-ratio: attr(width) / attr(height);
}

2. Reserve Space for Dynamic Elements

If your site displays dynamic content like banner ads, promotional alerts, or third-party widgets, do not let them push your content down when they load. Wrap them in a container with a defined minimum height.

.ad-container {
  min-height: 250px;
  background-color: #f9f9f9;
  display: flex;
  align-items: center;
  justify-content: center;
}

3. Use CSS font-display: swap Carefully

When loading custom web fonts, the browser may hide the text until the custom font file is downloaded (Flash of Invisible Text - FOIT) or render a fallback font and then swap it out (Flash of Unstyled Text - FOUT). If the fallback font has different character widths than your custom font, it will cause a layout shift when the swap occurs.

To prevent this, use font-display: swap but match your fallback font metrics as closely as possible to your primary font using modern CSS properties like size-adjust:

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom-font.woff2') format('woff2');
  font-display: swap;
}

The Third-Party Script Dilemma

One of the most common issues we see during our technical SEO services audits is a site buried under third-party scripts. Marketing teams require Google Tag Manager, Google Analytics 4, Hotjar, Meta Pixel, HubSpot, and chat widgets.

Each of these scripts adds heavy JavaScript execution costs, dragging down your Lighthouse score. While you cannot always remove these tools, you can manage how they load.

1. Delay Non-Critical Scripts Until First Interaction

Many tracking scripts do not need to execute during the initial page load. You can write a simple wrapper script that delays loading these assets until the user scrolls, moves their mouse, or touches the screen.

function loadThirdPartyScripts() {
  if (window.scriptsLoaded) return;
  window.scriptsLoaded = true;

  // Load your Google Tag Manager or other tracking scripts here
  const gtmScript = document.createElement('script');
  gtmScript.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX';
  gtmScript.async = true;
  document.head.appendChild(gtmScript);
}

// Listen for user interaction events
['mousedown', 'mousemove', 'keypress', 'touchstart', 'scroll'].forEach(event => {
  window.addEventListener(event, loadThirdPartyScripts, { once: true, passive: true });
});

2. Offload Scripts to Web Workers with Partytown

For advanced setups, you can use open-source tools like Partytown to run intensive third-party scripts (like the Meta Pixel or Google Analytics) inside a background web worker. This moves the execution off the main thread entirely, keeping your Total Blocking Time near zero.


Comparison: Optimization Techniques and Business Realities

Not every optimization technique offers the same return on investment. Some require days of development work for a minor performance increase, while others can be implemented quickly and yield significant results.

Optimization Technique Metric Targeted Development Effort Business Risk Real-World Impact
Preloading Hero Images LCP Low Very Low High
Adding Aspect Ratios to Media CLS Low None High
Delaying Non-Critical JS TBT / INP Medium Low (small tracking delay) Very High
Setting Up Edge Caching (CDN) LCP (TTFB) Low Low High
Rebuilding with SSR Frameworks LCP / TBT High High (requires migration) Massive
Aggressive Image Compression LCP Low Low (watch for visual degradation) Medium

If you are operating a complex, plugin-heavy site, optimization can only take you so far. If you are comparing a platform like Shopify vs custom eCommerce, you must weigh the out-of-the-box performance limitations of hosted templates against the speed advantages of a custom headless architecture.


Frequently Asked Questions

Why does my Lighthouse score change on every run?

Lighthouse runs simulate real network and CPU conditions. If your server experiences a brief latency spike (high Time to First Byte), or if your local CPU is handling background operating system tasks during the test, your score will fluctuate. Always run multiple tests and look at the average, or use server-side monitoring tools for consistent tracking.

Can I get a 100/100 score on WordPress or Shopify?

Yes, but it requires strict discipline. You must limit your reliance on heavy plugins, optimize your asset loading pipeline, avoid page builders that generate bloated HTML, and implement solid caching strategies. If your current site is too bloated to save, it may be time to consider a clean website redesign built on a lightweight, modern codebase.

Is a perfect 100/100 score required for SEO rankings?

No. Google’s algorithms look for healthy green thresholds rather than perfect scores. Once your Core Web Vitals are comfortably in the "Good" range (e.g., LCP under 2.5s, CLS under 0.1, and INP under 200ms), pushing for a perfect 100/100 offers diminishing returns. Your engineering time is better spent building features that drive business value.


Pragmatic Next Steps

Improving your Lighthouse score is not about chasing a vanity metric; it is about building a fast, reliable path for your users. Slow websites lose customers, drop in search rankings, and waste ad budget.

If you want to optimize your digital presence, start by diagnosing your current bottlenecks. You can use our custom web development expertise to audit your codebase, restructure your asset delivery pipeline, and eliminate main-thread blocking.

Ready to turn your performance metrics from red to green? Contact us today to discuss a pragmatic optimization plan or a complete platform modernization.

GOOGLE SEARCH CENTRAL SOURCE REPUTATION

Stay Updated via Google Preferred Sources

Add HWT Techy to your preferred sources in Google Search to receive verified updates and technical dispatches in Google Top Stories and AI Overviews.

FREE DIAGNOSTIC TOOL // INSTANT SCAN 30+ CWV CHECKS

Is Your Website Passing Core Web Vitals?

Enter your domain below to run our free, instant technical SEO audit scanner. Uncover slow LCP assets, layout shifts (CLS), and schema errors in seconds.

Need help with these strategies?

Our developer team builds custom websites, fast web apps, and Google search solutions.

Explore Services
Share Article
Start a Project