When it comes to modern web development, delivering a fast and seamless user experience is no longer optional—it's a must. Nuxt, a powerful framework built on top of Vue.js, offers a rich ecosystem for developing performant and scalable web applications.
However, to truly unlock its potential, developers must focus on fine-tuning performance at every level, from server-side rendering to client-side optimization. This article dives into the key strategies and best practices for optimizing performance in Nuxt applications.
We’ll explore how Nuxt’s architecture, including server-side rendering (SSR), static site generation (SSG), and its modular ecosystem, can be leveraged to build lightning-fast web apps. Topics like code splitting, lazy loading, caching, and fine-tuning build configurations will be covered, offering both foundational and advanced techniques to boost your app's speed and responsiveness.
If you are interested in learning more about how you can audit/measure performance of your Nuxt app in local development, Continuous Integration and also in production, check out this link.
Nuxt - a Vue framework
Before we start diving into specific Nuxt patterns for improving performance, we have to remember that Nuxt is a Vue framework for building modern web applications.
This means that the majority of Vue Performance improvements will also find usage here. You can learn more about How to Optimize Performance in Vue apps in our other article here.
Nuxt built-in performance features
Nuxt comes packed with several built-in performance features designed to make your applications fast and efficient right out of the box.
Here’s a rundown of some of the most notable ones:
- Automatic Code Splitting - Nuxt automatically splits your app's JavaScript into smaller chunks, loading only what’s needed for the current page. This reduces the initial load time and improves overall performance.
- Lazy Loading - Nuxt enables lazy loading for components, images, and assets to reduce initial page load times and improve user experience.
- Preloading and Prefetching - Nuxt intelligently preloads assets for the current page and prefetches resources for upcoming pages, ensuring faster navigation between routes.
- Efficient Build Process - Nuxt uses Webpack (or Vite in Nuxt 3) for bundling, with optimized configurations that prioritize performance, like tree-shaking and minification.
- Automatic CSS Extraction - CSS is extracted and served as separate files, reducing JavaScript bundle sizes and improving caching.
- Route Caching and Hybrid Rendering - Nuxt’s hybrid rendering allows different caching strategies for routes, enabling better performance and scalability.
- Integrated CDN Support - Easily configure a CDN for serving static assets, reducing latency and improving global performance.
- Compression - Nuxt supports middleware like gzip or brotli compression to deliver smaller files to users, enhancing load times.
- Head Management - Nuxt's
<head>
management ensures proper meta tag generation, which can optimize performance metrics like time to interactive (TTI) and Largest Contentful Paint (LCP). - Tree Shaking - Unused code is automatically removed during the build process, keeping your bundles lean.
- Nuxt Islands - Leverage Nuxt Islands to skip the hydration of certain components completely, reducing the overhead of rendering and improving performance.
- Server Components - Use Nuxt Server Components to render components on the server, reducing the amount of JavaScript sent to the client and improving performance.
These features make Nuxt a performance-oriented framework that caters to both developers and end-users, ensuring your apps load quickly and run smoothly with minimal effort.
Rendering modes
Rendering is the process of turning Vue.js components into HTML elements, and it can happen either in the browser or on the server. Nuxt offers several rendering modes to fit different project needs, including universal rendering, client-side rendering, hybrid-rendering, and even rendering your app on CDN Edge Servers.
Choosing a rendering mode can have an enormous impact on the performance of your website. The general rule here is that less code delivered to the browser the better - it will make the website load faster for your users.
Client Side Rendering (CSR)
Client-Side Rendering (CSR) is a web application rendering method where the content is generated in the user's browser using JavaScript, rather than being pre-rendered on a server or during the build process.
When a user visits a CSR-based website, the browser initially loads a minimal HTML file. Then, JavaScript files are then downloaded and executed to fetch and render the necessary content, and finally, the content is dynamically generated and displayed in the browser.
In CSR apps, content is fetched and displayed dynamically, making it ideal for applications that require frequent updates or user-specific data. CSR is commonly used in SPAs, where the entire app runs in the browser and navigates between "pages" without full-page reloads.
Since rendering happens in the browser, the server primarily serves static assets (HTML, JS, and CSS), reducing the computational load. However, all rendering is being done on the client-side (browser), which means that it is up to the user devices to render the website. Poor internet connection, CPU, and memory can drastically decrease the performance of CSR websites.
CSR is often used to create dynamic, user-focused applications. It works best for apps where interactivity and speed after the initial load are prioritized over fast first-page render times or SEO.
Server Side Rendering (SSR)
Server-Side Rendering (SSR) is a web application rendering method where HTML is generated on the server for each request and sent to the user's browser, instead of relying on the browser to dynamically generate content using JavaScript. This diagram explains how server-side rendering and hydration work for Nuxt.
In SSR applications, pages are rendered dynamically on each user request. While this ensures fresh and up-to-date content, it can become a bottleneck as the complexity of the app grows.
Generating pages on demand can lead to slower response times, especially for high-traffic apps. Although caching can help, managing it for large-scale apps, like an e-commerce site with millions of pages, can be challenging and resource-intensive.
SSR is ideal for applications where SEO and fast first-page load times are critical, such as e-commerce, news sites, or any app with user-specific or frequently updated content.
You can read more about it here.
Static Site Generation (SSG)
With Static Site Generation (SSG), pages are rendered once during the build process, creating static HTML files that are served directly to users, without any dynamic processing at runtime.
SSG provides performance benefits similar to Server-Side Rendering (SSR), with fast content delivery thanks to pre-rendered static files. It’s also more cost-effective and simpler to deploy compared to SSR, as it eliminates the need for a server to process requests dynamically. This diagram explains how Static Site Generation works.
However, SSG is best suited for pages with static data—information that doesn’t change frequently and is known at build time. Any updates to the content require a rebuild and redeployment of the site. SSG is an excellent choice for improving SEO on pages like /home
, /about
, or /contact
and works particularly well for content-driven websites, such as blogs or documentation sites.
On the other hand, SSG applications pre-render all pages at build time, which works well for smaller projects. However, as the number of pages increases, the build time can become unmanageable. For a site with thousands or millions of pages, pre-building everything can lead to significantly longer deployment times, making it impractical.
Incremental Static Regeneration
Incremental Static Regeneration (ISR) is a modern web concept designed to address the limitations of both SSR (Server-Side Rendering) and SSG (Static Site Generation).
ISR combines the best of both worlds by allowing pages to be built statically at first and then regenerated individually when necessary. This balances performance and scalability, making it ideal for modern web applications with dynamic content and large datasets.
With ISR, you can build your application as a static site initially but also update specific parts of it whenever needed—like when content in your CMS changes—without rebuilding the entire app.
With ISR, you get the scalability and speed of static sites alongside the flexibility of dynamic updates, making it an ideal solution for modern web apps that rely on frequent content updates.
Which rendering mode is the best?
So many rendering modes to choose from but which one is the best for your project? The answer is: “It depends”. There is no single best rendering mode for every project - thankfully, Nuxt allows to switch between rendering modes with a single line in your configuration or even set custom rendering rules like so:
export default defineNuxtConfig({
routeRules: {
// Homepage pre-rendered at build time
"/": { prerender: true },
// Products page generated on demand, revalidates in background, cached until API response changes
"/products": { swr: true },
// Blog post page generated on demand once until next deployment, cached on CDN
"/blog/**": { isr: true },
// Admin dashboard renders only on client-side
"/admin/**": { ssr: false },
},
});
Hybrid rendering in Nuxt gives you the flexibility to set different caching rules for each route using Route Rules. It lets you decide how the server should handle incoming requests for specific URLs. If you prefer more traditional Vue approach, you can also set rendering rules like so:
<script setup>
// Pre-render the homepage at build time
defineRouteRules({
prerender: true,
});
</script>
<template>
<div>
<h1>Homepage</h1>
<p>Pre-rendered at build time</p>
</div>
</template>
With Nuxt's built-in route rules and hybrid rendering support, you can customize how your app behaves. You can group routes together, adjust their rendering modes, or assign specific caching strategies to optimize performance and tailor the user experience.
Useful modules for better Nuxt performance
Nuxt comes with several performance oriented modules that will help you improve the speed of your application.
🎉
Some of them are official modules while others are built fully by community members and contributors.
Optimizing images with Nuxt Image
Nuxt Image is a powerful module in the Nuxt ecosystem designed to optimize and handle images efficiently in your Nuxt applications. It simplifies the process of managing responsive images, improving loading times, and boosting overall web performance.
With Nuxt Image, you can:
- Optimize Images Automatically - It adjusts image formats, sizes, and quality based on device and browser capabilities.
- Lazy Loading - Loads images only when they enter the viewport, reducing initial page load times.
- Responsive Images - Automatically generates and serves images in different sizes for various screen resolutions, such as mobile, tablet, and desktop.
- Built-in CDN Support - Integrates seamlessly with image CDNs like Cloudinary, ImageKit, or even your own setup, for fast delivery.
- Customizable Settings - You can easily define defaults for quality, sizes, and more while still allowing overrides for specific images.
The Nuxt Image
module provides a <NuxtImg>
component and useImage
composable that make integrating optimized images into your app both simple and efficient. By using it, you can deliver high-quality visuals without compromising performance, giving your users a fast and visually rich experience.
Optimizing fonts with Nuxt Fonts
Nuxt Fonts is a module for Nuxt.js that makes it easy to optimize and integrate web fonts into your Nuxt applications. It helps streamline font usage, improves performance, and simplifies font loading by managing them directly within your Nuxt project.
With Nuxt Fonts, you can:
- Easy Integration - Import and use fonts from popular services like Google Fonts, Adobe Fonts, or self-host custom fonts with minimal configuration.
- Automatic Optimization - Ensures only the necessary font files and weights are loaded, minimizing page load times and reducing unnecessary data transfer.
- Font Subsetting - Supports loading only the characters or glyphs needed for your application to reduce file size further.
- Preloading and Caching - Automatically preloads fonts and optimizes caching strategies for faster rendering and smoother user experiences.
- Simple Configuration - Fonts can be configured easily in the Nuxt config file (
nuxt.config.ts
), specifying font families, weights, and other options.
With Nuxt Fonts
, you can ensure your fonts are loaded efficiently, improving performance and ensuring your app looks great across all devices. It’s especially useful for projects that require consistent and polished typography without sacrificing speed.
Optimizing scripts with Nuxt Scripts
Nuxt Scripts makes it easy to load third-party scripts while enhancing performance, privacy, security, and developer experience (DX). It comes with built-in support for many popular third-party services, so you can integrate them seamlessly.
With Nuxt Scripts you get following benefits:
- Better Web Vitals - Load scripts only when necessary, using best practices to ensure they don’t block your Nuxt app’s rendering by default.
- Privacy for Your Users - Keep user data safe by preventing unnecessary data leaks to third-party scripts. Easily ensure your integrations are GDPR-compliant.
- Developer-First Experience - Enjoy type-safe, SSR-friendly composables that work effortlessly wherever you need them.
- Secure Third-Party Integrations - Safeguard your app from potential risks by ensuring third-party scripts are securely handled and protected from compromise.
Nuxt Scripts
adds an abstraction layer for third-party scripts, offering SSR support and type-safety while still giving you complete control over how scripts are loaded at a low level.
Taking control over SSR Hydration with Nuxt Lazy Hydrate
Nuxt Lazy Hydrate is a performance optimization feature in Nuxt that helps reduce the initial JavaScript payload by deferring the hydration of components until they are needed. It allows you to load and activate certain components only when they become visible in the viewport or are required, instead of loading and hydrating them upfront.
With Nuxt Lazy Hydrate you get following benefits:
- Improved Initial Load Performance - By deferring the hydration of components, Nuxt Lazy Hydrate helps reduce the initial JavaScript bundle size, leading to faster page load times.
- On-Demand Hydration - Components are only hydrated (i.e., turned from static HTML into interactive Vue components) when they come into view or when they're needed for user interaction. This means the app can be interactive sooner, without waiting for all components to be fully hydrated.
- Optimized User Experience - The approach ensures that users see content quickly, and only when necessary, dynamic components are hydrated to enhance performance and the overall user experience.
- Automatic Integration - Nuxt Lazy Hydrate works seamlessly with Nuxt's SSR (Server-Side Rendering) and SSG (Static Site Generation) features, ensuring that components are hydrated in an efficient manner across both server-rendered and static pages.
By using Nuxt Lazy Hydrate
, you can improve the performance of your Nuxt app, especially for complex pages with heavy or infrequently used components, making them load faster and feel more responsive.
Offload non-essential JS to web-workers with Nuxt Partytown
Nuxt Partytown is a performance optimization feature in Nuxt that helps offload non-essential JavaScript and resources to web workers, reducing the main thread workload and improving page load times. It integrates the PartyTown library into your Nuxt app, allowing you to move specific client-side heavy scripts and libraries (like third-party widgets, analytics, or embedded iframes) out of the main thread and into an iframe, which runs in a background web worker.
With Nuxt Partytown you get following benefits:
- Improved Performance - By shifting resource-heavy tasks to a web worker, PartyTown frees up the main thread, resulting in faster load times and smoother user interactions.
- Non-blocking Rendering - Scripts running inside PartyTown don't block the page from rendering, which means your app can display content to users much more quickly.
- Enhanced User Experience - Reduces jank (frame drops or delays in animations) and provides a smoother experience by offloading non-essential tasks from the main thread.
- Works with Third-party Scripts - Ideal for offloading third-party scripts (like Google Analytics, chat widgets, or embedded content) that don’t need to be executed directly on the main page.
By using Nuxt Partytown
, you can significantly boost the performance of your Nuxt app, particularly when dealing with external scripts and heavy client-side resources, without compromising on functionality or user experience.
Using DebugBear to audit Nuxt websites
The recommendations stated above should help you make your project more performant but let’s take a look at more practical example. Nuxt is used to power many modern websites and for this particular example, I have selected https://stackoverflow.co/
website.
Inspecting this website as a desktop user provides a really good experience. But what happens if we will simulate accessing this page using a device with throttled CPU, slow internet connection, and certain location? Let’s take a look at the below overview:
You can see all the details of the audit here.
We can clearly see that values of Performance metrics like First Contentful Paint (FCP), Largest Contentful Paint (LCP) and Speed Index (SI) are much higher than expected for the fast performing websites.
Run A Free Page Speed Test
Test Your Website:
- No Login Required
- Automated Recommendations
- Google SEO Assessment
Scrolling a bit further, we will see what element on the page was discovered as LCP. Usually, LCP is an image but in this case it is a text, specifically h1
. Going further, we can see recommendations listed by DebugBear that could help discovering areas to improve:
First three issues / recommendations are related to how the website handles images - large images, not lazy loaded images below the fold, not using modern image formats. Inspecting a website with VueTelescope browser extension gives information about what modules are used in this Nuxt / Vue project:
We can see that the NuxtImage
module is missing from this list. Nuxt Image automatically uses modern image formats, delivers responsive sized images, and allows to easily configure lazy loading as well as fetchPriority and preloading images if needed.
Summary
Nuxt is a powerful framework built on top of Vue.js that offers a rich ecosystem for developing performant and scalable web applications. By implementing performance optimization techniques listed above, you can improve the speed and responsiveness of your Nuxt application, enhancing the user experience and search engine rankings.
Keep in mind that in order to improve performance of your web application, you would also need to optimize performance of your HTML, CSS, and API to achieve a generally performant application.
Additional resources
If you would like to learn more about these concepts, please check out the following articles: