back to journal
core web vitals
Extreme Performance: Getting to Lighthouse 100 on Real Apps
Practical techniques for achieving perfect Lighthouse scores in production. No tricks, just engineering.
Ralph DuinJanuary 27, 20262 min read
<p>Perfect Lighthouse scores aren't just for toy demos. Here's how we consistently ship production apps that score 100 on SEO and 98+ on Performance.</p>
<h2>The Performance Budget</h2>
<p>Start with constraints:</p>
<ul>
<li>LCP (Largest Contentful Paint) ≤ 2.5s</li>
<li>INP (Interaction to Next Paint) ≤ 200ms</li>
<li>CLS (Cumulative Layout Shift) ≤ 0.1</li>
</ul>
<p>These aren't suggestions. They're requirements.</p>
<h2>1. Server-First Rendering</h2>
<p>Client-side JavaScript is the enemy of performance. Use Next.js Server Components by default. Ship HTML that works without JS.</p>
<pre><code>// ❌ Client-heavy
'use client'
export default function Page() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data').then(r => setData(r))
}, [])
return <div>{data?.title}</div>
}
// ✅ Server-first
export default async function Page() {
const data = await fetchData()
return <div>{data.title}</div>
}</code></pre>
<h2>2. Image Optimization</h2>
<p>Images kill performance. Use <code>next/image</code> with proper sizing:</p>
<pre><code><Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Above fold
sizes="(max-width: 768px) 100vw, 1200px"
/></code></pre>
<p>Key wins:</p>
<ul>
<li>Automatic WebP/AVIF format selection</li>
<li>Lazy loading below the fold</li>
<li>Responsive srcset generation</li>
</ul>
<h2>3. Font Loading Strategy</h2>
<p>Web fonts block rendering. Use Next.js font optimization:</p>
<pre><code>import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Prevent FOIT
preload: true,
})</code></pre>
<h2>4. Resource Hints</h2>
<p>Tell the browser what's coming:</p>
<pre><code><link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://api.example.com" /></code></pre>
<h2>5. Bundle Size Discipline</h2>
<p>Every dependency costs performance. Audit regularly:</p>
<pre><code>npx @next/bundle-analyzer</code></pre>
<p>Questions to ask:</p>
<ul>
<li>Do we really need this library?</li>
<li>Can we lazy load this?</li>
<li>Is there a lighter alternative?</li>
</ul>
<h2>6. Caching Strategy</h2>
<p>Use ISR (Incremental Static Regeneration) for content:</p>
<pre><code>export const revalidate = 3600 // Revalidate hourly</code></pre>
<p>Add Cloudflare in front for edge caching. Purge on content updates.</p>
<h2>7. Prevent Layout Shift</h2>
<p>Reserve space for dynamic content:</p>
<pre><code>// ❌ Causes CLS
<div className="dynamic-height">
{loading ? <Spinner /> : <Content />}
</div>
// ✅ Fixed height
<div className="h-96">
{loading ? <Spinner /> : <Content />}
</div></code></pre>
<h2>Monitoring in Production</h2>
<p>Lighthouse scores in CI don't guarantee real-user performance. Use Real User Monitoring:</p>
<ul>
<li>Vercel Analytics (built-in for Next.js)</li>
<li>Google Search Console (Core Web Vitals report)</li>
<li>Custom Web Vitals tracking with analytics</li>
</ul>
<h2>The Workflow</h2>
<ol>
<li>Lighthouse CI in PR checks (block on regressions)</li>
<li>Bundle size checks (block on growth > 5%)</li>
<li>Manual Lighthouse audit before deploy</li>
<li>Monitor Core Web Vitals post-deploy</li>
</ol>
<p>Perfect scores aren't magic. They're the result of disciplined engineering and performance-first architecture.</p>
<!-- related:/technical-seo -->
<h2>Further reading</h2>
<p>Performance is one half of the ranking story. For the full picture — crawlability, schema, AI search readiness, and a 90-day remediation roadmap — see the <a href="/technical-seo">technical SEO audit service</a>, which pairs Lighthouse diagnosis with SEMrush and DataForSEO signals.</p>