The Challenge
When building modern web applications, Next.js gives you a lot of performance features out of the box. But getting a perfect Lighthouse score, especially on mobile, requires careful attention to detail.
I recently optimized a content-heavy Next.js application that was struggling with a 65 mobile performance score. Here is exactly what moved the needle to 98.
Fonts and Images
These are usually the biggest culprits for poor LCP (Largest Contentful Paint) and CLS (Cumulative Layout Shift).
Fonts:
Images:
Code Splitting and Dynamic Imports
Next.js automatically code-splits by route, but you need to manually split heavy components that aren't immediately visible.
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("./HeavyChart"), {
loading: () => <p>Loading chart...</p>,
ssr: false // If it relies on browser APIs
});If you have a complex modal or a chart that sits below the fold, dynamically import it. Your initial bundle size will thank you.
Caching Strategy
The App Router's caching model is powerful but confusing. To get the best server response times (TTFB):
Third-Party Scripts
Analytics, customer support widgets, and ads will destroy your score.
Use next/script with the correct strategy:
The Results
After applying these changes, our mobile performance score went from 65 to 98. LCP dropped from 4.2s to 1.1s, and CLS was effectively zero.
The biggest lesson? Don't fight the framework. Use Next.js's built-in components (Image, Font, Script) as intended, and strictly manage your third-party dependencies.