LCP Nuxt 3+

Fix LCP in Nuxt

Largest Contentful Paint (LCP) is a critical Core Web Vital that measures how quickly the main content of your page becomes visible. In Nuxt applications, common issues include unoptimized images, render-blocking resources, and inefficient data loading patterns that delay the largest visible element.

This guide walks through five targeted fixes for LCP in Nuxt, with real code examples and before/after performance comparisons. Each step addresses a specific bottleneck in the Nuxt rendering pipeline.

Expected results

Following all five steps typically produces these improvements:

Before

3.8s

LCP score (Needs Improvement) -- unoptimized images, SSR without caching, heavy payload, blocking fonts

After

1.3s

LCP score (Good) -- NuxtImage with preload, static generation, optimized fonts, reduced payload

Step-by-step fix

Use NuxtImage for automatic image optimization

The @nuxt/image module provides <NuxtImg> and <NuxtPicture> components that automatically optimize images with responsive sizes, modern formats (WebP, AVIF), and lazy loading. For the LCP image, set loading="eager" and preload to generate a preload link in the HTML head.

Common mistake: Install @nuxt/image and add it to your nuxt.config.ts modules array. Without it, you lose automatic format conversion, responsive sizing, and provider-based optimization (Cloudinary, Imgix, etc.).
Vue -- Before (Bad)
Vue -- After (Good)

Enable static generation or ISR for content pages

Nuxt 3 supports multiple rendering strategies per route. Use prerender for static pages and isr for pages that need periodic updates. Static pages serve from CDN edge nodes with near-zero TTFB, while ISR regenerates pages in the background on a configurable schedule.

TypeScript -- nuxt.config.ts
export default defineNuxtConfig({
  // Route-level rendering rules
  routeRules: {
    // Static generation for content pages
    '/blog/**': { prerender: true },
    '/guides/**': { prerender: true },

    // ISR for dynamic pages: regenerate every 60 seconds
    '/products/**': { isr: 60 },

    // SSR with CDN caching for frequently accessed pages
    '/': {
      headers: {
        'Cache-Control': 'public, max-age=300, s-maxage=600, stale-while-revalidate=3600'
      }
    },
  },
});

Optimize font loading with @nuxt/fonts

The @nuxt/fonts module automatically optimizes web fonts by self-hosting them, generating font-face declarations with font-display: swap, and adding preload links. It supports Google Fonts, Adobe Fonts, and custom fonts with zero configuration.

TypeScript -- nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/fonts'],

  fonts: {
    // Auto-detected from CSS usage, or explicit:
    families: [
      { name: 'Inter', provider: 'google', weights: [400, 500, 600, 700] },
    ],
    defaults: {
      weights: [400, 500, 700],
      styles: ['normal'],
    },
  },
});

// The module automatically:
// 1. Downloads fonts and self-hosts them
// 2. Generates @font-face with font-display: swap
// 3. Adds preload links in the HTML head
// 4. Subsets to used unicode ranges

Reduce the Nuxt payload for faster hydration

Nuxt serializes server-fetched data into a __NUXT__ payload that the client uses for hydration. Large payloads slow down parsing and hydration, delaying LCP. Minimize the payload by selecting only needed fields in your API responses, using pick with useFetch, and avoiding passing raw API responses to the client.

Vue -- Before (Bad)
Vue -- After (Good)

Lazy-load below-the-fold components

Nuxt 3 supports lazy-loading components with the Lazy prefix. Components prefixed with Lazy are automatically code-split and loaded only when they are rendered. Use this for below-the-fold content to reduce the initial JavaScript bundle size and speed up hydration.

Vue -- Lazy components


Quick checklist

  • LCP image uses <NuxtPicture> with preload and loading="eager"
  • Content pages use prerender: true or isr in route rules
  • @nuxt/fonts module installed for automatic font optimization
  • useFetch calls use pick to minimize payload
  • Below-fold components prefixed with Lazy for code splitting
  • SSR responses have Cache-Control headers
  • Deployed to edge (Vercel, Netlify, Cloudflare)

Frequently asked questions

A well-optimized Nuxt 3 site using SSG or ISR should achieve LCP under 1.5 seconds. With NuxtImage preloading, @nuxt/fonts optimization, and edge deployment, sub-1.3-second LCP is achievable. CrUX data shows Nuxt sites have a 65% CWV pass rate, with the main bottleneck being payload size and hydration time.

Yes. Nuxt 3's route rules let you configure rendering strategy per-route. Static pages (prerender: true) achieve the best LCP because they serve from CDN edges with near-zero TTFB. ISR pages offer a balance between freshness and performance. SSR pages with Cache-Control headers can also achieve good LCP by caching at the CDN layer.

Both provide automatic optimization, responsive sizing, and lazy loading. NuxtImage has a unique advantage with its preload prop that adds link rel='preload' to the HTML head for the LCP image. NuxtImage also supports multiple providers (Cloudinary, Imgix, Vercel) for edge-based optimization. The key difference is that NuxtImage uses NuxtPicture for automatic format negotiation with the picture element.

The __NUXT__ payload is the primary hydration bottleneck. Reduce it by: using pick in useFetch to select only needed fields, using transform to process data server-side, avoiding nested relations in API responses, and using lazy components for below-the-fold content. Nuxt 3.8+ also supports payload extraction for better caching.

Both frameworks can achieve excellent Core Web Vitals. Nuxt's strengths include @nuxt/fonts for automatic font optimization, NuxtImage with preload, and flexible per-route rendering rules. Next.js offers React Server Components and a larger ecosystem. CrUX data shows Next.js at 68% CWV pass rate versus Nuxt at 65%, but well-optimized sites on either framework can score equally well.

Google rates LCP as 'good' when it is under 2.5 seconds at the 75th percentile. For Nuxt applications specifically, aim for under 2.0 seconds. Measure with field data from Chrome User Experience Report (CrUX) through PageSpeed Insights, as lab tests may not reflect real-user experience with third-party scripts and varying network conditions.

Continue learning