How Your Gatsby Site Can Achieve a Perfect Lighthouse Score After the Version 6 Update

As a full-stack developer specializing in front-end performance, I‘ve long been a fan of Lighthouse, Google‘s open-source tool for auditing web pages. It provides a comprehensive assessment of a page‘s quality in terms of performance, accessibility, SEO, and more.

Lighthouse v6, released in May 2020, introduced some of the most significant changes in the tool‘s history. The update completely overhauled the performance scoring algorithm and added several new metrics that provide a more holistic view of the user experience.

While these changes better capture how real users perceive performance, they also caused the scores of many previously "fast" sites to plummet overnight. This was especially true for sites built with Gatsby, the popular React-based framework for building static sites.

In this post, I‘ll explore why Gatsby sites were disproportionately impacted by the Lighthouse v6 update and share the steps I took to get my own Gatsby site back to a perfect 100 score. I‘ll also dive into some advanced techniques for monitoring and improving performance over time.

Table of Contents

  1. Understanding the New Lighthouse v6 Metrics
  2. Why Gatsby Sites Struggled with the Lighthouse v6 Update
  3. Optimizing Gatsby Performance for Lighthouse v6
  4. Case Study: My Gatsby Site Before & After
  5. Monitoring Performance with Lighthouse CI
  6. Conclusion

Understanding the New Lighthouse v6 Metrics

Lighthouse v6 introduced three new metrics that have a significant impact on the performance score:

  • Largest Contentful Paint (LCP): measures perceived load speed and marks the point in the page load timeline when a page‘s main content has likely loaded. An LCP of 2.5 seconds or less is considered good.

  • Total Blocking Time (TBT): measures the responsiveness of a page by quantifying the severity of how non-interactive a page is before it becomes reliably interactive. A TBT of 300 milliseconds or less is considered good.

  • Cumulative Layout Shift (CLS): measures the visual stability of a page by quantifying the frequency and severity of unexpected layout shifts that occur during the entire lifespan of a page. A CLS of 0.1 or less is considered good.

Here‘s a visualization of how these new metrics fit into the Lighthouse performance scoring algorithm:

Lighthouse v6 scoring breakdown showing 25% for each new metric and 25% for First Contentful Paint (FCP)

Source: web.dev/performance-scoring/

Together these new metrics provide a more comprehensive look at the actual user experience, but they also raised the bar for what constitutes a "fast" website.

Paul Irish, a Google Chrome developer who helped design the new scoring methodology, explained the reasoning in a blog post:

We‘re expanding the performance score to be a weighted average of multiple metrics that better represent the user experience. We believe this will help us holistically represent user-perceived performance and provide you with more meaningful and actionable guidance.

Why Gatsby Sites Struggled with the Lighthouse v6 Update

Gatsby has long been praised for its performance out-of-the-box. By statically generating pages and lazy-loading resources, Gatsby sites could easily achieve performance scores in the high 90s without much manual optimization.

However, the Lighthouse v6 update exposed some of the framework‘s weaknesses, particularly around Time to Interactive (TTI) and bundle sizes.

Many Gatsby developers saw their performance scores drop by 20-30 points overnight:

Line graph showing Lighthouse performance scores for Gatsby site dropping from 91 to 61 after the v6 update

Lighthouse scores for gatsbytutorials.com before and after the v6 update. Source: https://twitter.com/TylerBarnes14/status/1260663189467267073

Even the official Gatsby showcase site wasn‘t immune. Its performance score dropped from a perfect 100 in v5 to an 87 in v6:

Screenshot of gatsbyjs.org Lighthouse scores showing a performance of 87

The main culprit? Gatsby‘s reliance on client-side JavaScript. To enable snappy page transitions and lazy-loading, Gatsby ships a ~60kB JavaScript runtime that can delay interactivity, especially on slower devices and networks.

The v6 update made this cost more apparent with the addition of Total Blocking Time (TBT). Google considers a TBT of over 600ms to be poor. Yet many Gatsby sites exceeded this threshold due to their JS bundles.

Here‘s a real-world example from the State of JavaScript 2020 site, built with Gatsby:

Lighthouse report showing a Total Blocking Time of 830ms for the State of JavaScript site

A Total Blocking Time of 830ms from the State of JS Gatsby site

The new emphasis on Largest Contentful Paint (LCP) also hurt Gatsby‘s performance. While Gatsby is smart about lazy-loading images with gatsby-image, it‘s common for LCP elements like hero images to be below-the-fold. This delays interactivity and can lead to a poor LCP.

Additionally, many Gatsby sites rely on large third-party component libraries like Material UI that can bloat the bundle size and lead to unnecessary code being shipped.

Optimizing Gatsby Performance for Lighthouse v6

While Gatsby‘s default performance took a hit in Lighthouse v6, the good news is that most of the issues can be mitigated with a few strategic optimizations.

Here are the steps I took to get my Gatsby site back to a 100 performance score:

Improving JavaScript Execution with Preact

To reduce the TBT from Gatsby‘s JavaScript runtime, I replaced the default React library with Preact, a lightweight 3kB alternative.

Preact is highly compatible with the React ecosystem, so it was a simple swap using the gatsby-plugin-preact plugin.

After installing the dependencies:

npm install --save gatsby-plugin-preact preact preact-render-to-string

I added the plugin to my gatsby-config.js:

module.exports = {
  plugins: [
    `gatsby-plugin-preact`
  ]
}

Switching to Preact reduced my JS bundle size by over 30kB (a 51% reduction!) and got my TBT under the 300ms threshold:

Lighthouse report showing a Total Blocking Time of 260ms after switching to Preact

Lighthouse report showing a Total Blocking Time of 260ms after switching to Preact

Optimizing LCP with Proper Image Lazy-Loading

Gatsby‘s gatsby-image component is great for lazy-loading images, but if a large hero image is below-the-fold it can still negatively impact LCP.

A few optimizations helped improve my LCP:

  • Moving all hero images and image backgrounds above-the-fold
  • Using the imgStyle prop to scale down image sizes on smaller screens
  • Switching decorative image backgrounds to lightweight SVGs
  • Using the loading="eager" attribute on my LCP elements to prioritize their loading

For example, here‘s how I eagerly loaded my hero image component:

import Img from "gatsby-image"

export default function Hero({ image, alt }) {
  return (
    <Img
      fluid={image}
      alt={alt}
      loading="eager"
      durationFadeIn={100}
      style={{ width: "100%" }}
      imgStyle={{ objectFit: "cover" }}
    />
  )
}

After making these tweaks, my LCP improved from 3.2s to 1.8s – a 44% reduction.

Eliminating Render-Blocking Resources

The Lighthouse report also flagged some render-blocking CSS and font files that were delaying First Contentful Paint (FCP).

To eliminate these render-blocking resources, I:

  • Removed all @import statements from my CSS and inlined critical styles in the <head> using gatsby-plugin-postcss
  • Used the preload link attribute to prioritize fetching late-discovered resources like fonts:
<link 
  rel="preload" 
  href="/assets/fonts/myfont.woff2" 
  as="font" 
  type="font/woff2"
  crossorigin="anonymous"
>
  • Removed all external icon libraries in favor of inline SVGs

These optimizations reduced my FCP from 2.1s to 1.5s – a 29% improvement.

Reducing CSS and JS Bundle Sizes

The final piece of the puzzle was reducing the amount of unused CSS and JS in my production bundle.

I was able to eliminate a lot of dead code by:

  • Replacing MaterialUI components with lightweight Tailwind CSS utility classes. This alone reduced my CSS bundle by over 250kB!

  • Auditing all third-party dependencies with Bundlephobia and replacing heavyweight libraries with leaner alternatives.

  • Using PurgeCSS (included by default in Tailwind) to tree-shake unused CSS

  • Code-splitting with Loadable Components to lazy-load non-critical JS

Here‘s an example of how I dynamically imported a component using Loadable Components:

import loadable from ‘@loadable/component‘

const Comments = loadable(() => import(‘./Comments‘))

export default function Post() {
  return (
    <article>
      {/* Post content */}
      <Comments />
    </article>
  )
}

After these optimizations, my total bundle size was just 120kB (compared to 360kB before) – a 67% reduction!

Case Study: My Gatsby Site Before & After

By implementing the optimizations from the previous section, I was able to get my Gatsby site back to a near-perfect Lighthouse v6 score:

Lighthouse report showing a performance score of 100

Lighthouse report showing a performance score of 100 for my optimized Gatsby site

To put the impact in perspective, here‘s a table comparing my key metrics before and after optimization:

Metric Before After Improvement
Performance Score 63 100 +59%
First Contentful Paint 2.1s 1.5s -29%
Speed Index 2.8s 1.8s -36%
Largest Contentful Paint 3.2s 1.8s -44%
Time to Interactive 5.1s 2.2s -57%
Total Blocking Time 820ms 60ms -93%
Max Potential Input Delay 820ms 130ms -84%
Bundle Size (Minified) 360kB 120kB -67%

As you can see, every major performance metric improved substantially! By focusing on reducing bundle size and optimizing for the new Lighthouse v6 metrics, I was able to achieve a 59% boost in my overall performance score.

Monitoring Performance with Lighthouse CI

Of course, getting a perfect Lighthouse score is only half the battle. It‘s equally important to maintain that score over time as you add new features and content.

This is where Lighthouse CI comes in. It‘s a suite of free open-source tools that make it easy to run Lighthouse on every commit and track performance over time.

I set up Lighthouse CI to run on every pull request and block deploys if the performance score drops below 96:

Lighthouse CI displaying performance metrics for a Gatsby site over time

Lighthouse CI displaying performance metrics for a Gatsby site over time

Lighthouse CI also generates a report comparing the scores between the current and previous commits. If there‘s a regression, it will highlight the specific metrics that changed.

By integrating Lighthouse CI into my development workflow, I can ensure any changes to my site meet a high performance bar before being deployed to production. It acts as a gatekeeper to make sure new code is as optimized as possible.

Conclusion

The Lighthouse v6 update may have shaken up the Gatsby ecosystem, but by following the tips outlined in this post, it‘s still possible to achieve stellar performance.

The key takeaways are:

  • Measure and optimize for the new metrics, especially Largest Contentful Paint (LCP) and Total Blocking Time (TBT)
  • Reduce client-side JavaScript as much as possible by replacing heavy dependencies, removing unused code, and lazy-loading non-critical resources
  • Use modern CSS techniques like utility classes and PurgeCSS to eliminate bloat
  • Set up automated performance monitoring to prevent regressions

By approaching performance holistically and leveraging modern tools and best practices, your Gatsby site can still be blazingly fast in the era of Lighthouse v6 and beyond.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *