
Nuxt vs Astro (2026): benchmarks and an honest verdict
Astro shipped 126 B of gzip JavaScript versus Nuxt's 68.6 KB in our 51-page test. See the complete benchmark, tradeoffs, and honest verdict.

The short answer
Choose Astro when most of the value is in content and only parts of the page need JavaScript: marketing sites, editorial platforms, documentation, portfolios, landing pages, and many ecommerce frontends.
Choose Nuxt when the product behaves like an application: authenticated areas, shared client state, complex forms, real-time updates, route middleware, deeply interactive workflows, or a team already productive in Vue.
That sounds tidy on paper. Real projects are messier. Teams compare feature grids, rebuild the same polished homepage twice, and then argue over Lighthouse screenshots. I have been in those conversations, and they usually dodge the question that actually decides the architecture:
How much of this product genuinely needs to become a client-side application?
So I built the comparison I wanted to read: an equivalent 51-page fixture using the latest stable releases available on August 14, 2026, Astro 7.1.6 and Nuxt 4.5.1. On this content-heavy workload, Astro sent 126 bytes of executable JavaScript gzip for one small counter. Default Nuxt sent 68.6 KB gzip. Astro’s median production build took 410 ms, compared with 1,450 ms for Nuxt.
Those are real measurements, but they are not a universal leaderboard. They describe a deliberately content-heavy fixture, and that distinction is the point of this article.
Benchmark results first
I kept the fixture deliberately boring because boring is useful here. Both projects generated 50 article routes plus an index page. They used the same copy, the same 12-card grid, equivalent CSS, static generation, and one counter button. I built each framework three times in the same environment using Bun 1.3.14 and Node 25.1.0. The table reports the median build and the generated home page.
| Metric | Astro 7.1.6 | Nuxt 4.5.1 | Difference in this fixture |
|---|---|---|---|
| Median production build | 410 ms | 1,450 ms | Astro 3.5× faster |
| Build runs | 676 / 410 / 407 ms | 2,397 / 1,434 / 1,450 ms | Three runs each |
| Generated public output | 141,398 B | 254,279 B | Astro 44.4% smaller |
| Home HTML, gzip | 1,454 B | 1,862 B | Astro 21.9% smaller |
| Home CSS, gzip | 383 B | 380 B | Effectively equal |
| Initial executable JS, raw | 113 B | 183,445 B | Different runtime models |
| Initial executable JS, gzip | 126 B | 68,617 B | Astro avoids the app runtime |
Why is the gzipped Astro script larger than its raw 113 bytes? Compression has headers and is inefficient for something that tiny. That odd-looking number is a useful reminder to inspect the actual output instead of assuming every metric behaves intuitively.
The first run was slower for both frameworks, which is exactly why I report the median rather than choosing the most flattering number. Each run started with its previous build output removed, while package and system caches remained available as they would during normal local development or CI. The byte totals describe generated files, not network transfer for a real visitor. The page-level gzip figures are the more relevant comparison for initial delivery, and even those exclude images, fonts, caching behavior, and third-party scripts.
What I actually built
The benchmark application models a small content-heavy marketing or documentation site. It is not a blank starter and it is not a production application with unrelated integrations. The fixture has 51 statically generated routes:
- One index route with a heading, five paragraphs of identical copy, one counter button, and a responsive grid linking to 12 guides.
- Fifty detail routes named
guide-01throughguide-50. - Each detail route contains a link back to the index, a route-specific heading, and ten paragraphs created by repeating the same five-paragraph source twice.
- Both implementations use the same system-font stack, colors, spacing, content widths, three-column card layout, mobile breakpoint, and visible copy.
- Neither implementation loads images, web fonts, analytics, external APIs, a CMS, or third-party scripts.
The interaction is intentionally small. In Astro, the counter is 113 bytes of inline JavaScript that attaches one click listener and updates the button text. In Nuxt, the equivalent counter uses a Vue ref and template event binding. That is idiomatic for each framework, and it exposes the architectural distinction being tested: Astro can add one interaction without hydrating the page, while Nuxt hydrates the Vue application that owns the page.
Project structure and rendering mode
The Astro fixture contains a shared data.ts file, an index.astro route, and a dynamic [slug].astro route whose getStaticPaths() function creates the 50 guide pages. The production command is astro build, and the generated files are measured from dist.
The Nuxt fixture contains the same shared data, a minimal app.vue, an index.vue route, and a dynamic [slug].vue route. Nuxt prerenders the linked guide routes during nuxt generate, and the generated files are measured from .output/public. The Nuxt implementation uses NuxtLink, useHead, useRoute, and Vue reactivity because those are the normal framework primitives a real Nuxt project would use.
How each run was measured
I ran this sequence three times for each framework:
- Delete that framework’s previous production output directory.
- Start a timer immediately before the production build command.
- Run the build with console output suppressed so terminal rendering does not distort the timing.
- Stop the timer when the build process exits successfully.
- After the third build, inspect the generated public directory and home page.
The reported build duration is the median of those three runs. Output size is the sum of every generated public file. Home HTML is measured both raw and with gzip. CSS is taken from the home page’s emitted style blocks. Initial executable JavaScript includes inline executable scripts, referenced scripts, and module-preload scripts, while excluding JSON and import maps. Each JavaScript resource is gzipped individually before the compressed sizes are added, which is closer to how separate HTTP resources are transferred.
Both projects ran in the same environment with Bun 1.3.14 and Node 25.1.0. Their production dependencies were pinned: Astro 7.1.6, Nuxt 4.5.1, Vue 3.5.27, and Vue Router 4.6.4.
What the benchmark does prove
Here is what I am comfortable claiming: for a mostly static page, Astro’s default architecture makes an extremely small browser payload easy. An .astro component renders to HTML without becoming browser JavaScript. The counter uses a short native script, and the rest stays inert.
Nuxt rendered the same page to complete HTML, so this was not an SPA-versus-static straw man. But the default Nuxt page is also a Vue application. It ships the runtime and page chunks required to hydrate that application and support client-side navigation.
This matches the frameworks’ documented models. Astro renders framework components to HTML and CSS by default, adding client JavaScript only when a client directive asks for it. Nuxt uses universal rendering by default, then hydrates the Vue application in the browser.
What it does not prove
Here is what I am not claiming: Astro is always faster, 68 KB is automatically unacceptable, or a Nuxt application cannot achieve excellent Core Web Vitals. All three would be lazy conclusions.
It does not measure:
- A dashboard with ten interactive workflows.
- Authenticated navigation and route middleware.
- Live data, optimistic updates, or offline state.
- Server response time under load.
- Image optimization or third-party scripts.
- Developer time for a team already fluent in one framework.
- A tuned Nuxt build using
noScripts, server components, or carefully chosen route rules.
If the fixture were a project-management board instead of an article archive, Astro would need a substantial client island. The JavaScript gap would narrow because the product itself requires JavaScript.
That is not Astro losing. It is the workload telling the truth.
The architectural difference that actually matters
Astro asks: which components need to run in the browser?
Nuxt asks: how should this Vue application be rendered for the first request and subsequent navigation?
Both can generate static HTML. Both can render on a server. Both can call APIs, handle forms, and deploy to modern platforms. The difference is the default unit of interactivity.
Astro: HTML first, islands by exception
An Astro page can contain components, layouts, content collections, and server logic without automatically sending their implementation to the browser. A pricing calculator might use client:visible. A mobile menu can use a few lines of native JavaScript. A static testimonial grid remains HTML.
That gives content projects a strong performance property: adding another static component does not silently expand the hydration boundary.
Now for the part framework fans tend to leave out: teams can absolutely ruin this advantage. Add client:load to every React or Vue component, load a large consent manager, embed three marketing platforms, and ship unoptimized video, and the Astro logo will not save the page.
Astro is fast by default because it makes the performance-preserving decision natural. It does not make careless decisions impossible.
Nuxt: a coherent Vue application, rendered intelligently
Nuxt gives the browser and server a shared Vue application model. Pages, layouts, composables, middleware, plugins, state, and navigation fit together. That coherence is valuable when interaction is not a widget but the product itself.
That hydration cost is not waste by definition. It buys something:
- Stateful client navigation.
- Shared reactive data across routes and layouts.
- A consistent component and composable model.
- Mature application middleware and plugin conventions.
- Nitro server routes and deployment portability.
- Hybrid behavior through route rules.
Nuxt route rules can prerender one area, cache another with SWR or ISR, disable SSR for an application-only section, and even use noScripts where client JavaScript is unnecessary. That is far more nuanced than “Nuxt ships JavaScript, Astro does not.”
Nuxt also supports server-only components, although Nuxt documents server components and selective hydration as experimental. They can keep content rendering and heavy server-only dependencies out of the client bundle. I would use them deliberately, but I would not base a low-risk migration plan on experimental behavior without testing the exact deployment target.
Side-by-side: where each framework has the advantage
| Decision area | Astro | Nuxt |
|---|---|---|
| Content-heavy pages | Excellent default; little or no JS | Excellent HTML output, but default hydration adds runtime |
| Highly interactive application | Possible with islands, but architecture can fragment | Natural fit through Vue’s unified application model |
| Vue component reuse | Supported through integration and client directives | Native, complete Vue environment |
| Static generation | First-class | First-class with nuxt generate and prerendering |
| Per-route rendering | Static/server plus server islands | Very strong hybrid route rules, SWR, ISR, SSR, SPA |
| Content modeling | Built-in type-safe content collections | Usually Nuxt Content, a CMS, or custom data layer |
| Backend endpoints | Supported with adapters/on-demand routes | Nitro is a deeper full-stack application layer |
| Client state across pages | Add a framework island or browser store | Natural through Vue composables and state tools |
| Minimal browser runtime | Core architectural strength | Requires deliberate tuning and suitable routes |
| Existing Vue team | Familiar components, different page model | Lowest conceptual and migration friction |
| Hosting a purely static site | Simple static output | Simple through generated .output/public |
| Complex authenticated product | Use a large island or reconsider the boundary | Usually the clearer default |
The decision test I use before choosing
When I scope one of these projects, I do not start with page count. I count interactive surfaces.
For each route, mark every region that needs one or more of these:
- Persistent client state.
- Immediate updates after user input.
- Optimistic UI.
- WebSocket or real-time events.
- Browser-only APIs.
- Client-side access control or route middleware.
- Complex validation across multiple steps.
- Coordinated transitions between several components.
Then ask whether those regions are isolated or whether they form one connected application.
If the interactive regions are isolated
A search box, booking widget, pricing calculator, carousel, account menu, or product configurator can be a strong Astro island. The surrounding page stays HTML-first. Different islands can even use different UI frameworks, although I rarely recommend turning that flexibility into a zoo.
If the interactions share state and navigation
If a filter changes the URL, updates a table, changes a chart, persists a selection, affects the next route, and must survive authentication refreshes, it is no longer a small island. Nuxt’s application model will usually be easier to reason about.
This is the rule I keep coming back to:
When most of the page is the application, choose the application framework. When most of the page explains the application, choose the content framework.
Real-world scenarios
Marketing site with a CMS and lead forms
My choice: Astro.
Most pages should be stable HTML. Forms can post to an endpoint. Search, consent, menus, and calculators can be small islands or native scripts. The performance budget remains visible, and static output is easy to cache globally.
This is the pattern behind my own WordPress-to-Astro migration: preserve the useful content and URLs, replace template/runtime overhead, and keep dynamic behavior only where the site needs it.
SaaS dashboard
My choice: Nuxt.
The user is authenticated. Routes share state. Tables, filters, forms, notifications, permissions, and API calls interact. The Vue application runtime is doing real work instead of hydrating a mostly inert article.
The Fixture.cc tournament platform is a better example of Nuxt’s natural territory: format generation, administration, scheduling, capabilities, and stateful workflows are the product.
Documentation with an interactive playground
My starting choice: Astro, with a client island for the playground.
The documentation remains lightweight and indexable. The expensive editor/runtime loads only where it is used, ideally on visibility or explicit interaction rather than immediately on every page.
I would choose Nuxt instead if the documentation shares authentication, saved workspaces, account state, and navigation behavior with a larger Vue product.
Ecommerce storefront
It depends on the interaction boundary. I know that sounds like a consultant’s escape hatch, but ecommerce is exactly where a confident one-word answer should make you suspicious.
Astro is attractive when category and product pages dominate and the cart/account experience can be isolated. Server islands can defer personalized fragments without delaying the main page.
Nuxt becomes attractive when customer state, pricing, inventory, recommendations, checkout, localization, and account flows behave as one connected Vue application. Its hybrid rendering and Nitro layer may be worth more than the smallest possible landing-page bundle.
Blog or editorial publication
My choice: Astro, unless it is really a community application disguised as a publication.
Type-safe content collections, MDX, static output, and opt-in interactivity match the problem directly. Do not hydrate an entire application to render a byline and a table of contents.
Performance advice that survives either choice
The framework is only the first budget decision. I have seen carefully built Nuxt sites outperform careless Astro sites, because visitors download what you ship, not the philosophy in your README.
1. Measure JavaScript by route, not across the whole codebase
A codebase can contain a large dependency without sending it to every visitor. Inspect the assets referenced by representative production pages. Separate initial scripts, lazy chunks, prefetches, and third-party JavaScript.
Nuxt prefetching can make navigation feel fast but increases what the browser may fetch after the critical path. Astro can lazy-hydrate an island but still load a needlessly large package when it becomes visible. “Bundle size” without route and timing context is incomplete.
2. Treat hydration as an architecture boundary
Hydration is not merely a build setting. It determines which code runs twice, which data must serialize, where browser/server assumptions can diverge, and how much work a low-end phone performs.
In Astro, review every client:* directive. In Nuxt, review which pages need client behavior, which components can remain server-only, and where noScripts is safe.
3. Do not use client-only rendering to hide SSR bugs
<ClientOnly> and browser guards are useful for genuinely browser-dependent features. They are not a universal fix for hydration mismatches. Overuse delays content, can move CSS out of the initial response, and makes failures harder to see. Nuxt’s ClientOnly documentation explicitly notes that default-slot content is removed from the server build.
Fix unstable dates, random IDs, invalid HTML, state initialization, and browser/server branching at the source.
4. Optimize images and third parties before arguing over 20 KB
One unoptimized hero image or tag manager container can outweigh the framework difference. Define image dimensions, responsive sources, appropriate formats, lazy-loading below the fold, and a real policy for third-party scripts.
The right framework makes the budget easier to defend. It does not replace the budget.
5. Test the deployment mode you will actually run
Do not compare astro build static output with an untuned Nuxt development server. Do not compare cached edge HTML with a cold origin response. Decide whether the real product uses static generation, Node SSR, serverless functions, edge rendering, SWR, or ISR, then test that path.
Nuxt’s prerenderer crawls linked routes and writes static output. Astro also supports static and on-demand routes. The operational differences appear in cache invalidation, runtime APIs, adapter behavior, cold starts, and how content is updated, not in the existence of an HTML file alone.
Migration advice from someone who uses both
Moving from Nuxt to Astro
Please do not translate every page component line by line. That creates an Astro-shaped Nuxt application and preserves all the wrong boundaries. I first classify the existing Vue components:
- Static presentation.
- Isolated interaction.
- Shared application state.
- Server/data behavior.
Static presentation usually becomes Astro markup or framework components rendered without hydration. Isolated interaction can remain Vue with an explicit client directive. Shared state deserves a deliberate boundary; if most routes depend on it, the migration may be fighting the product.
Preserve URLs, canonicals, structured data, metadata, redirects, analytics events, forms, and actual rendered content. A framework migration is not successful because the home page looks the same.
Moving from Astro to Nuxt
This can make sense when isolated islands have quietly grown into a connected application. You usually notice it when three islands need the same store and start communicating through browser events. At that point, move the shared state and navigation model intentionally rather than wrapping every old component at once.
Decide which public routes should remain prerendered or cached. Nuxt does not require every page to become origin-rendered on every request. Use route rules to protect the behavior that made the Astro version fast.
Upgrading Nuxt 3 before comparing frameworks
If the real problem is an unsupported Nuxt 3 application, do not assume a framework rewrite is safer than an upgrade. A rewrite changes framework, rendering, content, routing, deployment, and often design at the same time.
My Nuxt 3-to-4 migration guide separates the supported-version upgrade from optional architectural cleanup. That is usually the lower-risk first move. Once the application is stable on Nuxt 4, the team can evaluate whether any content-heavy surface belongs in Astro with much clearer evidence.
Common comparison mistakes
“Astro is faster because it has no JavaScript”
Astro can ship no JavaScript. It can also ship a large React application. The meaningful claim is that Astro does not send component JavaScript unless you opt into client execution.
“Nuxt is bad for content sites”
It is not. Nuxt produces server-rendered HTML, supports prerendering, payload extraction, route caching, and excellent Vue-based editorial experiences. The question is whether its application model provides enough value to justify its default browser runtime for this project.
“SEO is better in framework X”
Search engines do not award rankings for framework logos. They see responses, rendered content, links, status codes, metadata, structured data, speed, and usefulness.
Astro can make low-JavaScript content delivery easier. Nuxt can produce fully indexable server-rendered or prerendered HTML. Either can ship broken canonicals, accidental noindex, orphan pages, or empty client-only content.
“We can decide from Lighthouse alone”
Lighthouse is a useful controlled test, not product strategy. Add field Core Web Vitals, conversion data, crawl behavior, build/deploy time, error rates, and the engineering cost of delivering the next year of features.
“The team already knows Vue, so Astro means throwing Vue away”
Astro can render Vue components and selectively hydrate them. But the page, routing, and data model still change. Vue reuse lowers migration cost; it does not make the two architectures identical.
My practical decision scorecard
If you are still torn, score the product you are actually funded to build, not the dream roadmap. Choose 1 when a statement is barely true, 2 when it is sometimes true, and 3 when it describes the core experience.
Rate each statement from 1 (barely true) to 3 (central to the product):
Most routes need shared client state rather than isolated widgets.
Authentication changes navigation, permissions, and page behavior.
Users spend long sessions moving through the product without full reloads.
Complex multi-step forms, dashboards, or coordinated interfaces are central.
Real-time events or optimistic updates are important to the experience.
The team already owns a substantial Vue component and composable ecosystem.
Your score
–/ 18
Score each item above to see your result.
- 6–10Your result
Astro is the stronger starting point. Keep interaction isolated and protect the content experience.
- 11–14Your result
Map the routes and shared state before choosing. You may have separate content and application surfaces.
- 15–18Your result
Nuxt is likely earning its runtime. The product behaves like a connected Vue application.
Treat the result as a conversation starter, not a final verdict. Its value is in showing where the project’s complexity actually lives. A middle score often reveals two distinct products within the same route map: a content surface and an application surface. In that situation, a split architecture or a deliberately tuned Nuxt application deserves a closer look.
The verdict
For the benchmarked content workload, Astro won decisively on emitted JavaScript, build time, and output size. That result is not surprising; the fixture sits directly inside Astro’s architectural sweet spot.
For a connected, authenticated application, I would still often choose Nuxt. Vue’s coherent client model, Nuxt’s route middleware, data conventions, hybrid rendering, and Nitro server layer can reduce product complexity far more than saving the framework runtime.
The wrong decision is not “choosing the slower framework.” It is choosing an architecture that treats the majority of the product as an exception. That exception becomes the part your team fights every sprint.
- If interaction is the exception, Astro makes that boundary explicit.
- If interaction is the product, Nuxt makes that system coherent.
- If you have both, map the routes and state before choosing one framework for ideological consistency.
Need a decision based on your actual application?
I work with both sides of this comparison: Nuxt and Vue application development, and WordPress-to-Astro or content-platform migrations.
Send me the route map, current stack, traffic profile, interaction model, deployment constraints, and the next twelve months of planned features. I will turn the framework debate into a concrete recommendation: stay on Nuxt, move to Astro, split the surfaces, or fix the implementation before changing frameworks. Sometimes the most valuable answer is that you do not need a rewrite.
Start a focused conversation and include the current application structure, not just the home page URL.


