Back to Blog
October 15, 2024·12 min read

Optimizing Next.js 15 for Core Web Vitals

Core Web Vitals are no longer just a ranking factor; they are the baseline for user retention. With Next.js 15, Vercel has introduced a suite of tools that make hitting that coveted 'All Green' Lighthouse score easier—if you know how to use them.

Next.js 15 Performance Architecture

The Shift to Partial Prerendering (PPR)

For years, React developers have faced a binary choice: Static Site Generation (SSG) for speed, or Server-Side Rendering (SSR) for personalization. SSG offered great Time to First Byte (TTFB) but stale data. SSR offered fresh data but slower initial loads.

Partial Prerendering (PPR) is the "Holy Grail" that combines both. It allows you to prerender a static shell (navbar, footer, sidebar) while streaming dynamic content (user profile, cart) in the same HTTP request.

Partial Prerendering Architecture Diagram

Implementing PPR in Next.js 15

To enable PPR, you use the experimental_ppr (now stable in v15) flag in your next.config.js. Then, wrap your dynamic components in Suspense boundaries.


// app/page.tsx
import { Suspense } from 'react';
import { UserCart } from './UserCart';
import { StaticProductList } from './StaticProductList';

export const experimental_ppr = true;

export default function Page() {
  return (
    <main>
      <h1>Welcome to the Store</h1>
      {/* This component is static and prerendered */}
      <StaticProductList />
      
      {/* This component streams in dynamically */}
      <Suspense fallback={<CartSkeleton />}>
        <UserCart />
      </Suspense>
    </main>
  );
}
            

This architecture results in a near-instant First Contentful Paint (FCP) because the static HTML is served immediately from the edge, while the dynamic bits pop in as they resolve.

Turbopack: Development Velocity at Scale

Performance isn't just about what the user sees; it's about how fast you can ship. Next.js 15 marks the full stabilization of Turbopack, a Rust-based successor to Webpack.

In our internal benchmarks migrating a monorepo with 500+ routes:

  • Cold Startup: 15s (Webpack) → 0.8s (Turbopack)
  • HMR (Hot Module Replacement): 2.5s → 0.1s
  • Memory Usage: Reduced by 40%

This speed difference fundamentally changes the developer experience (DX), keeping you in the "flow state" without standard waiting times.

Advanced Image Optimization Strategies

Images account for 50%+ of bytes on typical web pages. The next/image component has been upgraded, but using it blindly isn't enough.

1. Automatic format detection

Next.js 15 now prioritizes AVIF over WebP if the browser supports it. AVIF offers 20% better compression than WebP at the same visual quality.

2. The "sizes" prop is not optional

The most common mistake I see in code reviews is omitting the sizes prop on responsive images. Without it, Next.js assumes the image is 100vw, downloading a massive 3000px wide image for a mobile screen.


// BAD: Browser downloads potentially huge image
<Image src={hero}  alt="Hero" fill />

// GOOD: Browser knows exactly what to download
<Image 
  src={hero} 
   alt="Hero" 
  fill 
   sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
            

Server Actions and Caching

Next.js 15 simplifies the caching story. By default, fetch requests are no longer aggressively cached (a pain point in v14). You now opt-in to caching where it makes sense, rather than fighting the framework to opt-out.

Combine this with **Server Actions** for mutations, and you have a mental model that feels like standard web standards but runs with server-grade power.

Conclusion

Next.js 15 is less about new features and more about refining the primitives. By mastering PPR, optimizing your asset delivery pipeline, and leveraging the speed of Turbopack, you can build applications that feel native.


References

Optimizing Next.js 15 for Core Web Vitals | Akash Deep