Nuxt applications are fast by default and slow by accumulation. Almost nothing about a new project is a performance problem; the problems arrive gradually, through decisions that are individually reasonable. Most of the work is noticing them early, because each one is cheap to avoid and expensive to unwind.
Pick the rendering mode deliberately
The first decision is the one with the largest effect and it is usually made by default rather than on purpose.
This site prerenders every route to static HTML — ssr: true with a static Nitro preset. Marketing content changes on a human schedule, so there is no reason for a server to assemble the same page repeatedly. Crawlers get real markup, meta tags and structured data are present in the initial response, and hosting is a CDN with no runtime to keep warm or pay for.
That is the right default for content-shaped sites and the wrong one for an application whose content is per-user. Our product applications render differently for exactly that reason. The failure mode worth avoiding is not choosing either — accepting the default, then discovering that half the routes needed something else and patching around it per page.
Ship less JavaScript
The largest avoidable cost in most Vue applications is components that hydrate without needing to.
A component that renders static markup and never responds to interaction should not ship its JavaScript to the browser at all. Nuxt's lazy hydration and server components exist for this, and applying them to the obvious candidates — page sections, marketing blocks, anything below the fold that is purely presentational — reliably removes a meaningful share of the bundle.
The second avoidable cost is dependency weight. A date library imported for one format call, an icon set imported wholesale for six icons, a utility package for something the standard library now does. These are individually small and collectively dominant. Running a bundle analysis occasionally and asking whether each of the largest entries earns its size is a twenty-minute exercise that pays off repeatedly.
Images and fonts, once, properly
Images are usually the largest bytes on a page and the easiest to fix mechanically. Modern formats, explicit dimensions to prevent layout shift, lazy loading below the fold, and responsive sizes so a phone is not downloading a desktop asset. @nuxt/image handles this if you use it consistently; the failure mode is a few raw <img> tags that slip past.
Fonts are worth handling once and then forgetting. Self-host rather than pulling from a third-party origin, subset to the characters actually used, preload the faces that appear above the fold, and use font-display: swap so text renders before the font arrives. A custom display font is usually worth its cost; three weights of it usually are not.
Do not let animation cost frames
Our sites use motion fairly heavily, which makes this a live constraint rather than a theoretical one.
Animate transform and opacity and essentially nothing else — those are the properties the compositor can handle without layout or paint. Animating width, height, or top forces layout work every frame and shows up immediately on mid-range phones, which is what most visitors are using. Where an effect genuinely needs to animate something expensive, it is usually possible to fake it with a transform on a differently structured element.
Two things make this maintainable rather than a per-component negotiation. Keyframes and easing tokens live in one stylesheet, so motion is consistent and the expensive mistakes are caught in one place. And prefers-reduced-motion is honored throughout — an accessibility requirement first, and incidentally the cheapest frame budget available.
Measure the thing users experience
Lighthouse is a useful development tool and a poor description of production. It runs on one machine, on one network, with an empty cache.
Field data from real sessions is what tells you whether the work landed — Largest Contentful Paint and Interaction to Next Paint in particular, because they correspond to what someone actually notices: how long until the page is there, and whether it responds when touched. Those two numbers moving is the only evidence that matters, and they are frequently uncorrelated with the lab score that prompted the work.