Mastering Mobile Performance: A Complete Guide to Improving Next.js Lighthouse Scores

You've built a sleek Next.js application that runs beautifully on desktop browsers. The Lighthouse scores are impressive, and everything feels snappy and responsive. But when you test on mobile, it's a different story - performance scores plummet to 60-65, and your Largest Contentful Paint (LCP) crawls past 2.5 seconds.

If this sounds familiar, you're not alone. Many Next.js developers face this exact challenge, where mobile performance significantly lags behind desktop metrics. The good news? With the right optimizations and strategies, you can dramatically improve your mobile Lighthouse scores without compromising your application's functionality.

Understanding the Mobile Performance Gap

Before diving into solutions, it's crucial to understand why mobile performance often suffers compared to desktop. Mobile devices typically have:

  • Less processing power

  • Limited memory

  • Slower network connections

  • Different rendering behaviors

These limitations mean that performance optimizations that work well on desktop might not be enough for mobile users. Additionally, mobile users are more likely to access your site under suboptimal conditions - on spotty connections, older devices, or while multitasking.

Core Web Vitals: The Key Metrics

Google's Core Web Vitals are especially important for mobile performance. These metrics include:

  1. Largest Contentful Paint (LCP): Measures loading performance. For a good user experience, LCP should occur within 2.5 seconds of when the page first starts loading.

  2. First Input Delay (FID): Measures interactivity. Pages should have a FID of less than 100 milliseconds.

  3. Cumulative Layout Shift (CLS): Measures visual stability. Pages should maintain a CLS of less than 0.1.

On mobile devices, these metrics become even more critical. A layout shift that seems minor on desktop can be particularly frustrating on a smaller screen, while slow loading times can lead to higher bounce rates from mobile users.

Common Mobile Performance Killers

Through extensive research and developer feedback, we've identified several common issues that particularly impact mobile performance:

  1. Oversized images being served to mobile devices

  2. Excessive JavaScript bundles and static chunks

  3. Render-blocking CSS and JavaScript

  4. Unoptimized third-party scripts (especially ads and analytics)

  5. Poor font loading strategies

In the following sections, we'll explore practical solutions to each of these challenges, with specific Next.js optimizations that can help you achieve better mobile performance scores.

1. Optimizing Images for Mobile

Image optimization is one of the most impactful ways to improve mobile performance. As one developer pointedly noted, "DONT LOAD A 5000px WIDE IMAGE WHEN ITS BEING DISPLAYED AT 400px." This common mistake can severely impact your mobile performance scores.

Using Next.js Image Component

Next.js provides a powerful Image component that automatically handles many optimization tasks:

import Image from 'next/image'

function MyComponent() {
  return (
    <Image
      src="/my-image.jpg"
      alt="Description"
      width={400}
      height={300}
      priority={true} // For above-the-fold images
    />
  )
}

Key features of the Image component include:

  • Automatic image resizing for different devices

  • Modern format conversion (WebP/AVIF)

  • Lazy loading by default

  • Preventing layout shift through proper sizing

Best Practices for Mobile Image Optimization

  1. Properly Size Images

    • Use responsive sizes with the sizes prop

    • Implement proper breakpoints for different devices

    <Image
      src="/hero.jpg"
      sizes="(max-width: 768px) 100vw, 50vw"
      fill
      priority
    />
    
  2. Use Modern Formats

    • Enable WebP format in your next.config.js:

    module.exports = {
      images: {
        formats: ['image/webp'],
      },
    }
    
  3. Implement Proper Loading Strategies

    • Use priority for above-the-fold images

    • Lazy load images below the fold

    • Consider using blur placeholder for better perceived performance

2. JavaScript Optimization

One of the most common complaints from developers is the number of static chunk scripts loading on mobile devices. Here's how to address this issue:

Bundle Analysis and Optimization

First, install and configure the bundle analyzer:

npm install @next/bundle-analyzer

In your next.config.js:

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
  // your existing config
})

Implementing Dynamic Imports

Replace heavy static imports with dynamic ones:

// Before
import HeavyComponent from './HeavyComponent'

// After
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <p>Loading...</p>
})

Reducing JavaScript Payload

  1. Code Splitting

    • Use route-based code splitting

    • Implement component-level code splitting for large features

  2. Tree Shaking

    • Enable proper tree shaking in your build process

    • Remove unused exports

    // next.config.js
    module.exports = {
      webpack: (config, { dev, isServer }) => {
        if (!dev && !isServer) {
          config.optimization.minimize = true;
        }
        return config
      },
    }
    

3. CSS Optimization

CSS optimization is crucial for mobile performance, especially when using frameworks like Tailwind CSS. Many developers report issues with unused CSS blocking rendering and impacting performance.

Optimizing Tailwind CSS

  1. Purge Unused Styles

    // tailwind.config.js
    module.exports = {
      content: [
        './pages/**/*.{js,ts,jsx,tsx}',
        './components/**/*.{js,ts,jsx,tsx}',
      ],
      // other config
    }
    
  2. Split CSS by Route

    • Use CSS Modules for route-specific styles

    • Implement dynamic importing for CSS when possible

Critical CSS Extraction

Implement critical CSS extraction to improve First Contentful Paint:

// next.config.js
module.exports = {
  experimental: {
    optimizeCss: true, // This enables CSS optimization
  },
}

4. Font Optimization

Font loading can significantly impact mobile performance. As one developer noted, "removing fonts improved performance score from 64 to 79."

Implementing Efficient Font Loading

  1. Use Next.js Font Optimization

    import { Inter } from 'next/font/google'
    
    const inter = Inter({
      subsets: ['latin'],
      display: 'swap',
    })
    
  2. Consider System Fonts

    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 
                 "Helvetica Neue", Arial, sans-serif;
    
  3. Font Loading Best Practices

    • Use font-display: swap for better perceived performance

    • Preload critical fonts

    • Consider variable fonts for multiple weights

5. Third-Party Script Management

Third-party scripts, especially ads and analytics, can severely impact mobile performance. Here's how to optimize them:

Script Loading Strategies

  1. Use Next.js Script Component

    import Script from 'next/script'
    
    export default function Layout() {
      return (
        <>
          <Script
            src="https://www.googletagmanager.com/gtag/js"
            strategy="lazyOnload"
          />
        </>
      )
    }
    
  2. Implement Proper Loading Strategies

    • beforeInteractive: Critical scripts

    • afterInteractive: UI enhancement scripts

    • lazyOnload: Low-priority scripts

Optimizing Google AdSense and Analytics

  1. Defer Non-Critical Scripts

    <Script
      src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
      strategy="lazyOnload"
      crossOrigin="anonymous"
    />
    
  2. Consider Using Partytown

    • Move third-party scripts to web workers

    • Reduce main thread blocking

    // next.config.js
    const withPartytown = require('@builder.io/partytown/utils')
    
    module.exports = withPartytown({
      // your config
    })
    

6. Server-Side Optimization

Server-side optimizations can significantly impact mobile performance by reducing the time to first byte (TTFB) and improving overall response times.

Implementing Caching Strategies

  1. Static Generation

    // pages/blog/[slug].js
    export async function getStaticProps({ params }) {
      const post = await getPost(params.slug)
      return {
        props: { post },
        revalidate: 60, // Revalidate every minute
      }
    }
    
  2. Incremental Static Regeneration (ISR)

    • Use ISR for dynamic content that doesn't need real-time updates

    • Balance between static generation and server-side rendering

API Route Optimization

  1. Edge Runtime

    export const config = {
      runtime: 'edge',
    }
    
  2. Response Caching

    export default async function handler(req, res) {
      res.setHeader('Cache-Control', 's-maxage=10, stale-while-revalidate')
      // Your API logic here
    }
    

7. Monitoring and Testing

Regular monitoring is crucial for maintaining good mobile performance scores.

Implementation of Web Vitals Monitoring

export function reportWebVitals(metric) {
  if (metric.label === 'web-vital') {
    console.log(metric) // Send to analytics
  }
}

Testing Best Practices

  1. Regular Lighthouse Audits

    • Test on various mobile devices and networks

    • Use Chrome DevTools' mobile emulation

    • Monitor Core Web Vitals regularly

  2. Performance Budget

    // next.config.js
    module.exports = {
      experimental: {
        performanceBudget: {
          maxSize: {
            javascript: '170kb',
            css: '50kb',
          },
        },
      },
    }
    

8. Advanced Optimization Techniques

For those looking to squeeze out every last performance point, here are some advanced optimization techniques:

Implementing Service Workers

  1. Next PWA Setup

    // next.config.js
    const withPWA = require('next-pwa')({
      dest: 'public',
      register: true,
      skipWaiting: true,
    })
    
    module.exports = withPWA({
      // your next config
    })
    
  2. Custom Cache Strategies

    // public/service-worker.js
    workbox.routing.registerRoute(
      /^https:\/\/your-api\.com/,
      new workbox.strategies.StaleWhileRevalidate({
        cacheName: 'api-cache',
      })
    )
    

Resource Hints

Implement resource hints for better performance:

// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link
            rel="preconnect"
            href="https://fonts.gstatic.com"
            crossOrigin="anonymous"
          />
          <link
            rel="dns-prefetch"
            href="https://your-api.com"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

Optimizing for Core Web Vitals

  1. Layout Stability

    • Reserve space for dynamic content

    • Use CSS aspect ratio boxes

    .aspect-ratio-box {
      position: relative;
      padding-top: 56.25%; /* 16:9 Aspect Ratio */
    }
    
    .aspect-ratio-box-inside {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
    
  2. Input Latency

    • Debounce event handlers

    • Use requestAnimationFrame for smooth animations

    function debounce(func, wait) {
      let timeout
      return function executedFunction(...args) {
        const later = () => {
          clearTimeout(timeout)
          func(...args)
        }
        clearTimeout(timeout)
        timeout = setTimeout(later, wait)
      }
    }
    

Conclusion and Best Practices

Improving mobile Lighthouse scores in Next.js applications requires a comprehensive approach that addresses multiple aspects of performance optimization. Here's a final checklist to ensure you're covering all bases:

Quick Wins Checklist

✅ Implement Next.js Image component for all images ✅ Enable font optimization with next/font ✅ Implement proper script loading strategies ✅ Configure Tailwind purge for production ✅ Set up proper caching headers

Long-term Strategies

  1. Regular Monitoring

    • Set up continuous performance monitoring

    • Track Core Web Vitals in production

    • Regular Lighthouse audits on mobile devices

  2. Performance Culture

    • Establish performance budgets

    • Review third-party scripts regularly

    • Document optimization strategies

  3. Mobile-First Development

    • Test on real mobile devices

    • Optimize for low-end devices

    • Consider offline capabilities

Remember, mobile performance optimization is an ongoing process, not a one-time fix. Keep monitoring your metrics, stay updated with Next.js best practices, and continuously iterate on your optimizations.

By following these guidelines and implementing the suggested optimizations, you can significantly improve your Next.js application's mobile Lighthouse scores and provide a better experience for your mobile users.

Additional Resources

Start implementing these optimizations today, and watch your mobile Lighthouse scores improve dramatically!

Raymond Yeh

Raymond Yeh

Published on 13 March 2025

Get engineers' time back from marketing!

Don't let managing a blog on your site get in the way of your core product.

Wisp empowers your marketing team to create and manage content on your website without consuming more engineering hours.

Get started in few lines of codes.

Choosing a CMS
Related Posts
SEO Checklist for Next.js with App Router

SEO Checklist for Next.js with App Router

Boost your Next.js app's SEO with our guide! Learn to overcome technical challenges, use SSR/SSG, optimize images, and more to enhance search engine visibility.

Read Full Story
Testing in Next.js - What Should I Know?

Testing in Next.js - What Should I Know?

Complete guide to Next.js testing: Learn Jest, Vitest, React Testing Library, and end-to-end testing. Perfect for beginners with practical code examples and configuration steps.

Read Full Story
Should I Learn Next.js as My First Framework? A Developer's Guide to Making the Right Choice

Should I Learn Next.js as My First Framework? A Developer's Guide to Making the Right Choice

Should you learn Next.js as your first framework? Discover the essential path through JavaScript, React, and Node.js before tackling Server Side Rendering and middleware concepts.

Read Full Story
Loading...