Nuxt

Languages and frameworks | trial

Last updated:

trial

Mar 2026

Nuxt is a full-stack Vue framework for developing web applications. It takes the batteries-included approach and provides zero-configuration solutions to the common problems a complex web app has to address: page routing, server-side rendering (SSR), development and production server setups, code splitting, and many more. Nuxt is built on top of Vite, a frontend JS build tool, and Nitro, a production-grade server for Vite.

As with every opinionated tool, Nuxt comes with a pre-defined project structure and a set of conventions for writing code. Learning what goes where does take a bit of time, but the official documentation is well-structured and easy to navigate. Most of the Vue code lies in just two folders–pages/ and components/. The latter is a place for your regular Vue components, while each Vue file in pages/ is treated as a route in your web app, with the URL structure mirroring the folder and file names.

Server-side rendering

This leads us to one of the selling points of Nuxt—automatic routing and server-side rendering (called Universal rendering in Nuxt) by default. When a request comes to a Nuxt server, it finds the corresponding page file, runs its Vue setup function (where data is usually fetched) and performs the initial render on the server. Then, Nuxt responds with the resulting HTML along with the JS bundle. When the browser finishes loading the client-side JS, the resulting Vue application "takes over" the pre-rendered HTML in a process called Hydration. All subsequent routing within the app is done on the client, as in any other Single Page Application (SPA). The result is a fast, SEO-friendly initial response coupled with fluent page transitions and uninterrupted interactivity of an SPA.

Is it all smooth sailing then? Far from it. For example, the aforementioned hydration process may fail in unexpected ways if the client Vue app initializes with a DOM representation different from the server-rendered HTML. This results in a so-called hydration mismatch error and may cause loss of interactivity or sudden disappearance of parts of the page UI. While most hydration mismatches are caused by small errors in the template (for example, placing a <div> inside a <p> tag), some cases require a lot of effort to resolve.

SSR-related bugs are notoriously hard to spot and troubleshoot because the diversity of factors that come into play. While both the client and the server run the initial setup function, the resulting data might differ depending on the environment settings, responses from external APIs, and server- or client-specific branches or template blocks. Depending on your production setup, pages might be incrementally rendered and cached for a set amount of time, adding another variable to take into account.

There are even some SSR issues that might come up only occasionally, for instance, under high traffic. How is that possible? Since a single Nuxt server can potentially process multiple requests at once, and each request has its own context global object, Nuxt needs to track which context instance belongs to which request in order to pass it implicitly to Vue and Nuxt composables. When complex async/await logic is present, Nuxt sometimes loses the reference to a request's context and thus might fail to proceed with the normal render. While it is a known problem, we were surprised there isn't more guardrails forcing developers to deal with the issue before it lands on production.

Other rendering modes

Nuxt also provides a number of ways to render and deliver page content.

Client-side rendering In this mode, Nuxt behaves like a regular SPA app, with the server used to simply serve the assets and the client bundle.

Static site generation With the nuxt generate command, all pages are pre-rendered at build time, which can then be served as plain static files. This means Nuxt can be used in a Jamstack-like setting.

SSR and API cache Nuxt can also instruct the underlying Nitro server to cache pages with a certain TTL to avoid running SSR on every request. Additionally, you may define API handlers for fetching data and cache those, which can be used during both server and client renders.

Hybrid rendering In fact, all of the aforementioned rendering modes can be mixed in one Nuxt project! Each route or a route pattern can be customized depending on your needs using the routeRules option. For example, you might want to render static landing pages at build time (static: true), pre-render others during build but re-render them when cache is stale (prerender: true), use regular SSR for the rest (ssr: true), but keep some highly dynamic, SEO-irrelevant parts of the app (for instance, the Admin panel) as an SPA (ssr: false). In some extreme cases, all Nuxt-generated client-side JS can be removed for specific routes by setting noScripts: true.

SWR and ISR These aren't separate rendering modes, but rather cache and render strategies used during SSR. SWR stands for Stale While Revalidate and will instruct Nuxt to serve pages from the cache even if it is expired, while triggering a re-render in the background and updating the cache for future requests. ISR, short for Incremental Static Regeneration, is an option used by certain hosting providers to cache pages on their CDN. When TTL is provided, the option acts just like SWR but on a vendor-specific CDN instead of Nitro cache. Setting isr: true will basically cache the page until the next build—akin to static: true, but the initial generation happens on runtime when the page is first requested.

Edge-side rendering With some hosting providers (Vercel, Netlify, and Cloudflare Pages), we also can render them directly on the so-called edge nodes of the CDN, thus considerably reducing latency. ESR is enabled via a Nitro preset setting and can be combined with other hybrid rendering and caching options described above.

There is a panoply of other ways to customize server-side rendering behavior in Nuxt. When used thoughtfully, these options allow you to choose the optimal rendering and caching strategy for each piece of your application, going for maximum performance while keeping compute and storage costs in check.

The module system

Another highlight of Nuxt is its large collection of modules. They provide additional features and integrations in a way that follows the works-out-of-the-box philosophy of plain Nuxt. Some modules are maintained by the Nuxt team itself, while others come from third-party developers.

Most popular frontend tools and Vue libraries have official Nuxt modules, such as Tailwind CSS, PrimeVue, and Pinia. So do many SaaS tools and headless CMSes, including Algolia, Strapi, and Shopware (check out our Shopware Frontends radar entry for more on that integration).

Overall, we've had a pleasant experience with modules, especially the Nuxt-provided ones. Because of Nuxt's well-defined architecture, the interaction between modules and your Nuxt project happens in predictable ways, and all configuration lives in the same nuxt.config.ts file. However, as with any plugin system that includes third-party code, the quality might vary widely. For that reason, we advise to evaluate each module thoroughly before relying on it in production.

Conclusion

We think Nuxt is a powerful tool that may greatly help with building a robust Vue web application. Yet, it's not a do-it-all solution. Nuxt works well for large web applications that need to interface with multiple data sources and other web services, where fast page loading times and SEO are critical. Due to its complexity, teams working with Nuxt must have a good grasp of Vue and Nuxt inner workings and generously cover the application with unit, integration, and E2E tests.

If you want to see how Nuxt fares as an eCommerce storefront for Shopware 6, read our blog post about Nuxt-powered Shopware Frontends.

Related Items