Explore
Nuxt Performance Optimisation: The Practical Checklist

Nuxt Performance Optimisation: The Practical Checklist

A fast Nuxt app isn't an accident — it's the result of specific decisions at every layer. Here's the checklist we work through for every production application.

Nuxt Performance Optimisation: The Practical Checklist

Performance is a product feature. A slow page loses users — Akamai's data puts it at a 7% conversion drop per second of delay. A fast page retains them, ranks better in search, and signals quality.

Here's the complete checklist for Nuxt application performance.


Images

Images are typically the largest contributor to page weight and the easiest win.

Use @nuxt/image:

<NuxtImg
  src="/hero.jpg"
  width="1200"
  height="600"
  format="webp"
  loading="lazy"
  sizes="sm:100vw md:50vw lg:1200px"
/>

This automatically:

  • Converts to WebP (50-70% smaller than JPEG)
  • Generates responsive sizes
  • Lazy-loads images below the fold
  • Prevents layout shift by reserving space

Preload your LCP image:

// In your page component
useHead({
  link: [
    {
      rel: 'preload',
      as: 'image',
      href: '/hero.webp',
      fetchpriority: 'high'
    }
  ]
})

The LCP (Largest Contentful Paint) element is usually the hero image. Preloading it is often the single biggest improvement to your LCP score.


JavaScript bundle size

Large JavaScript bundles block rendering and delay interactivity.

Analyse your bundle:

npx nuxi analyze

This opens a visual treemap of your bundle. Look for:

  • Large libraries included multiple times
  • Libraries included when only a small subset is used
  • Vendor code that could be split or deferred

Tree-shaking: Make sure you're importing only what you use:

// Bad — imports the entire library
import * as _ from 'lodash'
_.map(arr, fn)

// Good — imports only the function you use
import { map } from 'lodash-es'
map(arr, fn)

Defer non-critical scripts:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: 'https://analytics.example.com/script.js',
          defer: true,           // Load after HTML parsed
          // or
          async: true,           // Load without blocking
        }
      ]
    }
  }
})

Critical rendering path

Inline critical CSS: For above-the-fold content, inline styles prevent a render-blocking stylesheet request. Nuxt handles this automatically for CSS-in-JS; for external stylesheets, consider extracting critical CSS.

Avoid render-blocking scripts: Scripts without defer or async block HTML parsing. Check your <head> for synchronous scripts that could be deferred.


Route prefetching

Nuxt automatically prefetches linked pages when they appear in the viewport. This makes navigation feel instant:

<!-- Prefetched automatically when visible in viewport -->
<NuxtLink to="/pricing">Pricing</NuxtLink>

To prefetch programmatically:

const router = useRouter()
// Prefetch /pricing without navigating
router.prefetch('/pricing')

API response times

Frontend performance is limited by your API speed. If a page waits for a slow API call to render, no amount of frontend optimisation compensates.

Move data fetching to the server: With SSR, fetch data in the server route handler and render it into the initial HTML. Users see content immediately rather than loading states.

Cache slow API responses:

// server/api/products.get.ts
export default defineCachedEventHandler(async () => {
  const data = await fetchFromDatabase()
  return data
}, {
  maxAge: 60 * 5, // Cache for 5 minutes
  staleMaxAge: 60 * 60 // Serve stale for up to 1 hour while revalidating
})

Static site generation for marketing content

For pages that don't need dynamic data (landing page, pricing, blog posts), pre-render them at build time:

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/pricing': { prerender: true },
    '/blog/**': { prerender: true },
    '/app/**': { ssr: true } // Dynamic app routes use SSR
  }
})

Pre-rendered pages are served directly from a CDN as static HTML — sub-100ms response times globally, no server load.


Font optimisation

Web fonts cause layout shifts and block rendering if not handled correctly.

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2-variations');
  font-display: swap; /* Show fallback while font loads */
  font-weight: 100 900;
}

font-display: swap prevents the invisible text problem. The browser shows a fallback font immediately and swaps when the custom font loads.

If using Google Fonts, switch to self-hosting with Fontsource for better control and performance:

npm install @fontsource-variable/inter
// In your Nuxt plugin or layout
import '@fontsource-variable/inter'

Core Web Vitals targets

MetricGoodNeeds ImprovementPoor
LCP< 2.5s2.5s - 4s> 4s
CLS< 0.10.1 - 0.25> 0.25
INP< 200ms200ms - 500ms> 500ms

Measure with:

  • Chrome DevTools Lighthouse — local testing
  • PageSpeed Insights — real-world data from Chrome users
  • Google Search Console — aggregated field data for your site

Run Lighthouse on every major release. Set up an alert if your scores drop significantly.


The 80/20

If you only do three things from this list:

  1. Use @nuxt/image for all images
  2. Prerender marketing pages
  3. Analyse and reduce your JavaScript bundle

These three typically account for the majority of performance improvement with the least effort.

Building a fast, well-optimised product on Nuxt? Let's work on it →