My "Lightweight" Next.js Site Scored 59 on Lighthouse. Here's What Actually Fixed it.
Learn how to increase your overall website performance. Loading speed and experience are huge factors for getting organic results on Google. Learn how to address stubborn load time issues with websites.

Intro
My site is light. The server responds in a blazing 20ms. So why did Google Lighthouse hand me a soul-crushing 59?
That was the exact paradox sitting on my screen last week. Server-Side Rendering was dialed in, edge functions were flying, and static routes resolved almost instantaneously. Yet Lighthouse flagged my site with a muddy yellow-orange score.
The problem stems from a fundamental misunderstanding of web performance: a fast server does not equal a fast user experience. Time to First Byte (TTFB) is just opening the door; what happens inside the browser main thread once the client receives the HTML is where performance scores go to die.
How to Actually read a Lighthouse Report
Most people see the big red or yellow score, curse under their breath, and immediately start re-engineering their API routes or stripping CSS libraries.
That score is a symptom; the audits are the diagnosis.
To actually fix performance, stop staring at the main number and jump straight into the Diagnostics and Opportunities sections:
- Expand "Largest Contentful Paint element": Locate the exact DOM node holding up visual rendering.
- Inspect "Reduce JavaScript execution time": Identify which third-party script is hogging thread time.
- Check "Properly size images": Calculate how many unnecessary megabytes are being shipped to mobile viewports.
Once you read the report as a trace rather than a report card, the primary culprits become obvious.
Suspect #1: The LCP image
Largest Contentful Paint (LCP) measures how long it takes for the main hero element above the fold to render.
A common oversight is serving a raw 1280×960 PNG cover photo inside a visual card displayed at 362px on a mobile viewport. Browsers are forced to download an uncompressed payload over mobile networks and scale it down on the fly.
To resolve this in Next.js:
- Pass the
priorityprop to preload the hero asset in the document<head>. - Set accurate
sizesso the browser requests the appropriate asset width. - Route images through Sanity or CDN transforms to deliver WebP/AVIF formats dynamically.
Before: Unoptimized Image Tag
// ❌ Unoptimized image fetch causing delayed LCP
<img
src={post.mainImage.url}
alt={post.mainImage.alt}
className="w-full h-auto"
/>After: Optimized Next.js Image
// ✅ Optimized LCP image with priority and responsive sizing
import Image from 'next/image';
import { urlFor } from '@/lib/sanity';
<Image
src={urlFor(post.mainImage).width(800).format('webp').url()}
alt={post.mainImage.alt}
width={800}
height={450}
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px"
className="w-full h-auto object-cover"
/>Suspect #2: The third-party tax
AdSense and Google Analytics often weigh more than an entire lightweight codebase combined.
Adding an async tag to third-party scripts is not enough. An async script still downloads with high priority and executes as soon as it arrives, hijacking the single browser thread while parsing heavy JavaScript bundles.
To reclaim main thread performance:
- Establish preconnect links in your
<head>to establish early DNS and TLS connections. - Defer non-critical analytics and ad scripts using the
next/scriptcomponent withafterInteractiveorlazyOnloadstrategies.
Resource Preconnect Snippet
<!-- Establish early handshakes with critical third-party origins -->
<link rel="preconnect" href="https://www.googletagmanager.com" />
<link rel="preconnect" href="https://pagead2.googlesyndication.com" crossorigin="anonymous" />Next/Script Deferred Loading Snippet
import Script from 'next/script';
export default function ThirdPartyScripts() {
return (
<>
{/* Load GA4 after the page becomes interactive */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"
strategy="afterInteractive"
/>
{/* Defer ad scripts until idle window */}
<Script
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXXXXX"
strategy="lazyOnload"
crossOrigin="anonymous"
/>
</>
);
}| Fix | Effort | Expected Gain |
|---|---|---|
| Add priority & sizes to LCP Image | Low (5 mins) | +15 to +25 pts |
| Defer Ads / Non-essential Scripts (lazyOnload) | Low (10 mins) | +10 to +20 pts |
| Convert Images to WebP/AVIF via CDN | Low (15 mins) | +5 to +10 pts |
| Add Preconnect Tags for Foreign Domains | Minimal (2 mins) | +3 to +8 pts |
| Font Subsetting & font-display: swap | Medium (20 mins) | +2 to +5 pts |
Results
Addressing asset delivery and execution order significantly changes the metric profile:
- Before Fixes: Lighthouse Score 59 | LCP 4.2s | Total Blocking Time 680ms
- After Fixes: Lighthouse Score 96 | LCP 1.1s | Total Blocking Time 40ms
No core architectural overhaul or framework swap was required—just explicit resource prioritization.
Wrapping Up
The uncomfortable truth about web development today is that ads and tracking scripts fund the ecosystem while directly penalizing performance scores. Google sits on both sides of that table—docking search rankings for poor Core Web Vitals while providing the very ad scripts that degrade them.
You don't need to strip away functionality to achieve high scores. By taking control of browser loading priorities, you can deliver fast user experiences without sacrificing monetization or analytics.
1 Comment
Sign in to join the discussion.
What big changes have you seen that worked for you?