Nuxt gives you SSR and static generation out of the box, but there are several things that can hurt your PageSpeed score. Here's how to optimize a Nuxt site for top performance.
Nuxt is a powerful full-stack Vue framework with server-side rendering and static generation built in. When configured correctly, it produces fast, SEO-friendly sites. When not, it can suffer from the same hydration overhead and bundle bloat as any other SPA framework.
Here's how to get your Nuxt site scoring in the 90s.
Nuxt supports multiple rendering strategies and choosing the right one has a huge impact on performance.
Static Site Generation (SSG) with nuxt generate is the fastest option. Pages are pre-rendered at build time and served instantly from a CDN. If your content doesn't change on every request, use SSG.
Server-Side Rendering (SSR) renders pages on the server per request. Slightly slower than SSG but necessary for dynamic, user-specific content. Use a fast server and edge deployment.
Client-Side Rendering (CSR) should be avoided for public-facing pages. It ships all your JavaScript before showing content, which hurts FCP and LCP.
Set your rendering mode in nuxt.config.ts:
export default defineNuxtConfig({
ssr: true, // or false for CSR
});
For mixed needs, use Nuxt's route rules to set rendering per route:
export default defineNuxtConfig({
routeRules: {
"/": { prerender: true },
"/blog/**": { prerender: true },
"/dashboard/**": { ssr: true },
},
});
The @nuxt/image module handles responsive images, WebP conversion, and lazy loading automatically.
npm install @nuxt/image
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["@nuxt/image"],
});
Use <NuxtImg> instead of raw <img> tags:
<NuxtImg
src="/hero.jpg"
width="1200"
height="630"
alt="Hero"
preload
/>
Add preload to your LCP image and never add loading="lazy" to it. For all other images, lazy loading is applied automatically.
Nuxt auto-imports components and composables, which is convenient but can lead to importing more than you need. Check your bundle size:
nuxt analyze
Common bundle bloat causes:
import * from instead of named importsUse component-level code splitting by marking heavy components as lazy:
<LazyHeavyComponent v-if="show" />
Nuxt won't include LazyHeavyComponent in the initial bundle.
useAsyncData and useFetch CorrectlyData fetching that runs on both client and server can cause double fetching, adding unnecessary network requests and slowing hydration.
<script setup>
// This runs on the server and result is passed to client
// No double fetching
const { data } = await useAsyncData("posts", () => $fetch("/api/posts"));
</script>
Always await your data fetching in setup so it completes before the component renders. This prevents layout shifts from content appearing after hydration.
Self-host your fonts instead of loading from Google Fonts. Use the @nuxtjs/google-fonts module if you want to keep using Google Fonts but serve them from your domain:
npm install @nuxtjs/google-fonts
export default defineNuxtConfig({
modules: ["@nuxtjs/google-fonts"],
googleFonts: {
download: true, // downloads and self-hosts fonts
families: {
Inter: [400, 600, 800],
},
display: "swap",
preload: true,
},
});
Use Nuxt's useHead or useScript (Nuxt 3.9+) to load third-party scripts without blocking the main thread:
// For analytics and non-critical scripts
useHead({
script: [
{
src: "https://analytics.example.com/script.js",
defer: true,
},
],
});
With @nuxt/scripts (experimental):
const { load } = useScript("https://analytics.example.com/script.js", {
trigger: "idle", // loads when browser is idle
});
In your Nitro server config, enable compression:
export default defineNuxtConfig({
nitro: {
compressPublicAssets: true,
routeRules: {
"/_nuxt/**": {
headers: { "cache-control": "max-age=31536000, immutable" },
},
"/**": {
headers: { "cache-control": "s-maxage=3600" },
},
},
},
});
Nuxt's Nitro server supports edge deployment on Cloudflare Workers, Vercel Edge, and similar platforms. Edge deployment dramatically reduces TTFB for global audiences.
# Deploy to Cloudflare Pages
NITRO_PRESET=cloudflare-pages nuxt build
@nuxt/image installed, using <NuxtImg> for all imagespreload attribute<LazyComponentName>@nuxtjs/google-fonts with download: truedefer or load on idleNuxt sites can comfortably score 90+ with the right configuration. The biggest wins come from choosing the right rendering mode, optimizing images, and managing your JavaScript bundle.
Test your Nuxt site to see your current PageSpeed score and find out what's affecting each metric.
How fast is your site?
Get your PageSpeed score in seconds — free, no sign-up needed.
Test Your Site →