Back to Blog

Get a 90+ PageSpeed Score on Next.js (Guide)

A step-by-step guide to optimizing your Next.js application for Google PageSpeed Insights. From image optimization to code splitting — everything you need for a 90+ score.

CallDevs Team
CallDevs TeamPerformance
10 min read read·December 20, 2024
Get a 90+ PageSpeed Score on Next.js (Guide)

Google PageSpeed Insights is the industry standard for measuring website performance. A score of 90+ isn't just bragging rights — it directly impacts your search rankings, user experience, and conversion rates.

At CallDevs, every site we build achieves a 90+ PageSpeed score. Here's exactly how we do it with Next.js, step by step.

Understanding What PageSpeed Measures

Before optimizing, you need to understand the metrics. PageSpeed Insights measures six Core Web Vitals:

  • Largest Contentful Paint (LCP): How long until the main content is visible. Target: under 2.5 seconds.
  • First Input Delay (FID): How long until the page becomes interactive. Target: under 100ms.
  • Cumulative Layout Shift (CLS): How much the page layout shifts during loading. Target: under 0.1.
  • First Contentful Paint (FCP): Time until the first content appears. Target: under 1.8 seconds.
  • Interaction to Next Paint (INP): Responsiveness to user interactions. Target: under 200ms.
  • Time to First Byte (TTFB): Server response time. Target: under 800ms.

Each metric is weighted differently, but LCP and CLS typically have the biggest impact on your overall score.

Step 1: Optimize Images with next/image

Images are usually the biggest performance bottleneck. Next.js provides the Image component that automatically optimizes images, but you need to use it correctly.

Always specify width and height attributes to prevent layout shift. Use the priority prop on above-the-fold images (hero images, logos). Set appropriate sizes prop to serve different image sizes for different viewports. Use WebP or AVIF format — next/image handles this automatically.

The sizes prop is critical and often overlooked. Without it, Next.js may serve images larger than necessary. For a three-column grid, you'd use sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw".

Step 2: Optimize Fonts with next/font

Custom fonts are a common source of layout shift and slow loading. Next.js's built-in font optimization with next/font eliminates these issues by self-hosting fonts and applying size-adjust to prevent CLS.

Import your fonts from next/font/google and apply them using CSS variables. This ensures fonts are loaded optimally with no external requests to Google's servers.

Always use the display: 'swap' option so text remains visible while fonts load. Define a fallback font stack that closely matches your custom font's metrics.

Step 3: Implement Code Splitting

Next.js automatically code-splits by route, but you can further optimize by dynamically importing heavy components. Any component that's not needed on initial page load should be dynamically imported.

Common candidates for dynamic imports include modals and dialogs, charts and data visualizations, rich text editors, map components, and heavy animation libraries.

Use next/dynamic with ssr: false for client-only components that don't need server-side rendering.

Step 4: Optimize Third-Party Scripts

Third-party scripts (analytics, chat widgets, marketing pixels) are performance killers. They block rendering, add to JavaScript bundle size, and often load synchronously when they don't need to.

Use the Next.js Script component with appropriate loading strategies. For analytics, use the afterInteractive strategy. For chat widgets and non-critical scripts, use the lazyOnload strategy. Never use beforeInteractive unless absolutely necessary.

Consider whether you really need each third-party script. Every script you remove improves performance. We've seen sites improve by 20+ points just by removing unused scripts.

Step 5: Implement Server-Side Rendering Strategically

Not every page needs the same rendering strategy. Next.js 14's App Router gives you fine-grained control over this.

Use Static Generation (default in App Router) for pages that don't change frequently — marketing pages, blog posts, documentation. Use Server-Side Rendering for pages that need fresh data on every request — dashboards, user profiles, real-time data.

Server Components in the App Router render on the server by default, which means less JavaScript sent to the browser. Only add 'use client' to components that truly need client-side interactivity.

Step 6: Optimize CSS

Tailwind CSS is already well-optimized because it purges unused styles in production. But there are additional optimizations you can make.

Avoid CSS-in-JS libraries in Server Components — they add unnecessary JavaScript overhead. Keep your Tailwind configuration lean by only including the plugins you actually use. Use CSS containment on layout sections to help the browser optimize rendering.

Step 7: Set Up Proper Caching Headers

Caching is one of the most impactful optimizations and often overlooked. Configure your hosting platform to set proper Cache-Control headers.

Static assets (images, fonts, JS bundles) should have long cache times — at least one year with immutable. HTML pages should have shorter cache times or use stale-while-revalidate.

If you're deploying on Vercel, most of this is handled automatically. For other platforms, you'll need to configure this in your next.config.js or hosting configuration.

Step 8: Minimize Layout Shift

Cumulative Layout Shift (CLS) is often the hardest metric to optimize. Common causes of layout shift include images without dimensions, dynamically injected content, web fonts causing text reflow, and ads or embeds loading after page content.

For images, always specify width and height. For dynamic content, use skeleton loading states that match the final content dimensions. For fonts, use next/font with size-adjust. For ads and embeds, reserve space with min-height.

Step 9: Use a CDN

If you're deploying on Vercel, you already have a global CDN. For other hosting providers, set up a CDN like Cloudflare or AWS CloudFront. A CDN reduces TTFB by serving content from servers geographically close to your users.

Step 10: Monitor and Iterate

Performance optimization isn't a one-time task. Set up monitoring to catch regressions. Use Vercel Analytics or Google's web-vitals library to track real-user metrics. Run Lighthouse CI in your deployment pipeline to catch performance regressions before they reach production.

Review your PageSpeed scores monthly and after every major feature addition. New features, third-party integrations, and content changes can all impact performance.

Quick Wins Checklist

Here's a rapid-fire list of quick wins that collectively make a big difference:

  • Enable gzip/brotli compression on your server
  • Remove unused npm packages from your bundle
  • Use SVGs instead of PNGs for icons and illustrations
  • Preconnect to required origins in your document head
  • Minimize the number of network requests on initial page load
  • Avoid layout thrashing by batching DOM reads and writes
  • Use will-change CSS property sparingly for animated elements

Need Professional Help?

If your Next.js site isn't hitting 90+ on PageSpeed, we can help. Our performance optimization service includes a comprehensive audit, implementation of all optimizations, and a guarantee that your site will score 90+ or we keep working until it does.

Web DevelopmentPerformance

Building a scalable web app?

Our engineering team specializes in Next.js, MERN, and fixing AI-generated code.

Book a Code Audit

Ready to Build Something That Actually Works?

Book a free 30-minute strategy call. No sales pitch — just honest advice about your project.