Letícia Caldeirão
•2025-11-28
A slow website is a dead website. Google reports that 53% of mobile users abandon sites that take over 3 seconds to load. That's why achieving high Google Lighthouse scores isn't just about bragging rights—it's about business survival.
At Backpack Works, we've optimized dozens of Next.js applications to achieve perfect or near-perfect Lighthouse scores. Here are our battle-tested strategies that actually work.
Before diving into optimizations, let's understand what we're optimizing for:
Core Web Vitals:
• LCP (Largest Contentful Paint): Should be under 2.5 seconds
• FID (First Input Delay): Should be under 100 milliseconds
• CLS (Cumulative Layout Shift): Should be under 0.1
Other Key Metrics:
• FCP (First Contentful Paint): When content first appears
• TTI (Time to Interactive): When the page becomes usable
• TBT (Total Blocking Time): Time the main thread is blocked
Images are usually the largest performance killer. Next.js's Image component is your secret weapon.
Before:
<img src="/hero.jpg" alt="Hero image" />
After:
import Image from 'next/image'
<Image
src="/hero.jpg"
alt="Hero image"
width={1920}
height={1080}
priority // For above-the-fold images
placeholder="blur"
blurDataURL={blurDataUrl}
/>
Pro Tips:
• Use WebP format (30% smaller than JPEG)
• Set explicit width and height to prevent CLS
• Use priority for hero images
• Implement blur placeholders for better perceived performance
Don't load code users don't need. Use dynamic imports to split your bundles.
import dynamic from 'next/dynamic'
// Only load heavy component when needed
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
loading: () => <div>Loading chart...</div>,
ssr: false
})
This can reduce your initial bundle by 30-50%.
Fonts can cause layout shifts and slow loading. Next.js 13+ has built-in font optimization:
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Prevents invisible text
preload: true,
variable: '--font-inter'
})Static pages are fastest. Use ISR (Incremental Static Regeneration) for dynamic content:
export async function getStaticProps() {
const data = await fetchData()
return {
props: { data },
revalidate: 3600 // Regenerate every hour
}
}Use the bundle analyzer to find bloat:
npm install @next/bundle-analyzer
Copy code
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
})
module.exports = withBundleAnalyzer({})
Run with: ANALYZE=true npm run build
Common fixes:
• Replace moment.js with date-fns (70% smaller)
• Use lodash-es with tree shaking
• Lazy load heavy components
Configure caching headers for static assets:
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:all*(svg|jpg|jpeg|png|gif|ico|webp)',
headers: [{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable'
}]
}
]
}
}
Third-party scripts can destroy performance. Load them strategically:
import Script from 'next/script'
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive" // Load after page is interactive
/>
Strategy options:
• beforeInteractive: Critical scripts
• afterInteractive: Important but not critical
• lazyOnload: Can wait until idle
Prevent layout shifts by:
Setting image dimensions:
<Image width={400} height={300} ... />
Reserving space for dynamic content:
.skeleton {
min-height: 200px; /* Reserve space */
}
Using CSS aspect-ratio:
.video-container {
aspect-ratio: 16 / 9;
}Lighthouse primarily scores mobile performance. Key optimizations:
• Use responsive images with sizes attribute
• Reduce JavaScript for mobile devices
• Test with CPU throttling enabled
• Minimize main thread work
Lab data isn't everything. Monitor real user experience:
import { getCLS, getFID, getLCP } from 'web-vitals'
function sendToAnalytics(metric) {
// Send to your analytics
console.log(metric)
}
getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getLCP(sendToAnalytics)Here are actual improvements we've achieved for clients:
• E-commerce site: 45 → 98 Performance score, 35% increase in conversions
• SaaS platform: 62 → 95 Performance score, 50% reduction in bounce rate
• Marketing site: 71 → 100 Performance score, 2x improvement in Core Web Vitals
Start with these for immediate improvements:
○ Replace <img> with Next.js <Image>
○ Add priority to above-the-fold images
○ Implement font optimization with next/font
○ Enable static generation where possible
○ Lazy load heavy components
○ Add proper caching headers
○ Optimize third-party script loading
○ Set explicit dimensions on images and videos
○ Remove unused JavaScript and CSS
○ Test on mobile with throttling
Achieving perfect Lighthouse scores in Next.js isn't magic—it's methodical optimization. Start with images (biggest impact), then tackle JavaScript bundle size, and finally fine-tune with caching and lazy loading.
Remember: Performance is a feature, not a nice-to-have. Every 100ms of improvement can increase conversions by 1%.
At Backpack Works, we don't just build websites—we build fast websites. Need help optimizing your Next.js application? Let's talk about transforming your Lighthouse scores and your business metrics.