Web performance with Nuxt: What actually improves load time
A fast website does not begin with compressing individual files. It begins with deciding what the browser needs for the first visible content. If the hero, type, and navigation appear only after a large JavaScript bundle runs, a smaller icon set will not rescue the opening experience.
This website combines Nuxt 4, Markdown content, GSAP animation, smooth scrolling, and an optional 3D scene. That mixture is interesting and also risky. Each technology can earn its place. Together, they need a budget and explicit rules.
Measure before optimising
Google describes user experience through three stable Core Web Vitals:
- Largest Contentful Paint, or LCP, measures when the largest visible content renders. A good result is 2.5 seconds or less.
- Interaction to Next Paint, or INP, evaluates response to user input. A good result is 200 milliseconds or less.
- Cumulative Layout Shift, or CLS, measures unexpected movement. The good range ends at 0.1.
These thresholds apply at the 75th percentile of page visits. The official Web Vitals documentation maintains the current definitions and metric lifecycle.
Lighthouse gives me a reproducible lab baseline. Field data matters more after launch because it includes real devices, networks, and interactions. A green desktop Lighthouse report does not prove that a site is fast on mobile.
For each important route, I capture at least:
- mobile Lighthouse results with a fixed profile
- the LCP element and its loading path
- JavaScript transferred on the first visit
- long tasks on the main thread
- layout shifts during loading and interaction
- behaviour with reduced motion enabled
The critical path determines LCP
The browser should discover the LCP element in the initial HTML. For a hero image, that means a real img with src or srcset, known dimensions, and appropriate priority. If client-side code inserts the image later, the download starts later than it needs to.
Optimize Largest Contentful Paint breaks LCP into server response, resource load delay, transfer time, and render delay. This is more useful than a blanket instruction to make images smaller. An image can be fully downloaded and still wait because JavaScript blocks the main thread or the page hides it until hydration finishes.
On a typical marketing site, I prioritise:
- complete HTML for the headline, subline, and call to action
- an early, discoverable hero resource
- small critical styles
- JavaScript only when genuine interaction begins
Nuxt supports universal rendering, static generation, and rules per route. Its documentation on rendering modes explains the trade-offs. Prerendered HTML is usually a strong base for stable marketing content, while dynamic API routes can remain server-rendered.
Hydration is a deliberate budget
Hydration connects rendered HTML to Vue logic in the browser. Interactive components need it, but every section does not need it immediately. A text block, price comparison, or footer should not start its own client lifecycle on first load without a reason.
I sort components into three groups:
- content that works as complete HTML
- interaction that can wait until visibility or input
- critical interaction such as navigation that must respond immediately
Nuxt 4 supports deferred hydration strategies for lazy components. The current option is documented under Nuxt experimental features. "Experimental" also means I use it only with tests and a measured benefit. A lower JavaScript total is not useful if the first click then stalls.
The better question is: when does this component need to become interactive? The answer determines whether hydration happens immediately, on visibility, or after input.
Animation should not keep the browser busy forever
Good motion comes from timing and hierarchy. A permanent loop is not evidence of quality. It becomes particularly expensive when JavaScript reads layout values and immediately writes styles in every frame. The browser repeats work even when nothing meaningful changes for the user.
My animation rules are concrete:
- prefer
transformandopacitywhen the design allows it - measure geometry after resize or content changes, not in every frame by default
- stop animation when an element is offscreen or the tab is hidden
- load canvas and 3D on small devices only when they have a clear purpose
- make continuous tickers pausable
- provide a complete static state for
prefers-reduced-motion
The W3C technique for prefers-reduced-motion explains how motion-sensitive users can request less animation through their system settings. I do not treat that as an accessibility switch added at the end. The static state belongs in the component design from the start.
Images need more than a modern format
AVIF or WebP often reduces transfer size, but format solves only part of the problem. The image still needs appropriate dimensions for the viewport, otherwise a phone downloads the same asset as a large desktop. Explicit width and height also prevent layout shifts.
Hero images follow different rules from a gallery:
- Do not lazy-load the likely LCP image.
- Deliver it early and with appropriate priority.
- Defer images below the fold.
- Make
srcsetandsizesmatch real layout widths. - Write alt text when the image conveys information.
Image quality should remain visible. A muddy hero saves bytes and loses trust. I look for the lowest size that remains clean in its actual layout instead of exporting every image at one quality setting.
Performance is architecture too
One line rarely creates a large client bundle. More often, a global component imports a library needed on only one route. A chat may fully initialise even when nobody opens it. Route-based splitting, dynamic imports, and delayed startup can matter more than micro-optimisation.
This connects directly to AI system architecture. Clear boundaries do more than keep providers replaceable. They also let us load and measure expensive features separately.
I ask four questions about every global dependency:
- Does every route need this code?
- Must it run before the first visible content?
- Can the feature load after an interaction?
- Do listeners or animations remain active after leaving the page?
The last problem does not appear in a static screenshot. During a real visit, work can accumulate across several navigations and make the interface gradually slower.
A performance gate for launch
A release needs measurable boundaries. For this website, the mobile gate is at least 90 for Lighthouse Performance and at least 95 for Accessibility, Best Practices, and SEO on the homepage and website service page. Field targets remain LCP at or below 2.5 seconds, INP at or below 200 milliseconds, and CLS at or below 0.1.
One run is not enough. I test several cold-cache runs, document the median and variance, then inspect the primary interactions. After launch, Search Console and field data show whether the lab assumptions hold for real visitors.
If you need a site where design, content, and delivery are planned together, the project options are on the Websites page. For a specific project, the shortest route is to start a conversation.
About the author
I am Kirk Kleinau, an AI Strategist and Creative Technologist. I have spent eight years working across design, concept work, project management, web, and frontend engineering. In my own builds, performance is part of the design because a visual idea only works when it reaches the user's device in time.