What is Nuxt? Learn more about the intuitive Vue framework
Nuxt solves the problem of structuring your Vue project as it comes with an enterprise-ready frontend development architecture.
Nuxt is the most intuitive Vue framework available today. It combines the power of Vue with server-side rendering features to make it more powerful. You can build a complete client-side rendered Vue application, a comprehensive static generated application, a server-rendered application, and a full-stack application with its own API.
Nuxt solves the problem of structuring your Vue project as it comes with an enterprise-ready frontend development architecture. The features of Nuxt are already structured using industry standards to create enterprise applications.
This guide will explore Nuxt 4, the current major version, what you can build with it, and how it works from the inside out.
What is Nuxt?#
Nuxt (formerly Nuxt.js) is a full-stack framework built on Vue.js. It abstracts away most of the complex configuration involved in managing asynchronous data, middleware, and routing. It also helps structure Vue.js applications using industry-standard architecture for building simple or enterprise Vue.js applications.
Under the hood, Nuxt uses Vite to build your application and the Nitro server engine to run it. Nuxt 3, released in November 2022, rebuilt the framework on Vue 3 and TypeScript, and Nuxt 4, released in July 2025, moved your application code into a dedicated app/ directory. If you're coming from Nuxt 2, which reached end of life in June 2024, many of the concepts below will look familiar, but most of the APIs have changed.
What is Nuxt used for?#
Nuxt allows you to create many different kinds of applications. Below are the most popular applications made with Nuxt.
Static generated pages#
This type of website does not require any external content source — the content is already embedded in the HTML. Examples of such websites include portfolio, demo, and tutorial websites.
Nuxt can pre-render every page of your site to HTML at build time with the nuxt generate command.
Single-page applications (SPA)#
This frontend development approach fetches dynamic content from an external API and displays it on the client-side. Most JavaScript frameworks such as React.js, Vue.js, Angular, and Ember.js are all single-page application frameworks.
What's more, a SPA's routing system is achieved with HTML 5 History API or the location hash for routing, which gives developers the ability to modify a website's URL without a full page refresh.
In Nuxt, you get a single-page application by turning off server-side rendering with ssr: false in your nuxt.config.ts file.
Universal applications#
"Universal application" is a term that describes the use of a server-side rendering approach to fetch client-side data on the server before fully rendering the page on the client browser.
Server-side rendering (SSR) is a known issue in Vue.js because it involves tedious configurations to get it right.
Nuxt solves the SSR problem in Vue.js, which is helpful for search engine optimization (SEO). Server-side rendering is on by default in Nuxt. Nuxt can even extend universal applications to full-stack applications, where the frontend and backend share a single codebase: any file you add to the server/api directory becomes an API endpoint.
Nuxt also supports hybrid rendering, where you choose how each route is rendered with routeRules in nuxt.config.ts. For example, you can pre-render a page at build time, cache another one on the server, and render a dashboard only in the browser:
export default defineNuxtConfig({
routeRules: {
'/services': { prerender: true },
'/posts': { swr: 3600 },
'/dashboard/**': { ssr: false },
},
})How does Nuxt work?#
Nuxt works the same way a server-side framework works when a user visits a website. If server-side rendering is enabled, the requests are rendered on the server each time the user requests a page, therefore a server is needed to be able to serve the page on each request. Also, if client-side rendering is enabled, it renders the content of the page in the browser using JavaScript.
These are the main pieces involved in rendering a page in Nuxt:
- Plugins: Functions in the
app/pluginsdirectory that run when the Nuxt app is created, before anything else. They're useful for registering Vue plugins and providing helpers to the rest of your app. - validate(): A function you can define on a page with
definePageMeta. It checks the route's dynamic parameters before the page is rendered, and Nuxt shows a 404 page if it returnsfalse. - Route middleware: Functions that run before Nuxt navigates to a page. They're handy for checks such as whether a user is logged in.
- useFetch() and useAsyncData(): Composables that fetch data on the server while the page renders. Nuxt sends the result to the browser with the page, so the data isn't fetched twice.
For instance, when a Nuxt site receives an initial page visit, the Nitro server starts rendering the page by running your plugins. If the route is dynamic, it then calls the page's validate() function, and if the parameters aren't valid, it returns a 404 page.
Next, it runs the route middleware in this order. First, it runs any global middleware, which are the files in the app/middleware directory that end in .global. Then, it runs the middleware the page asks for in its definePageMeta.
After the middleware, the page's useFetch() and useAsyncData() calls fetch the data on the server. Nuxt renders the page to HTML and sends it to the browser, along with the fetched data.
By this point, the visitor can see the full page. Vue then takes over the page in the browser, a process called hydration, reusing the data Nuxt sent instead of fetching it again. From then on, navigating between pages happens in the browser, like in a single-page application.
No matter which tool you use, you will always feel more confident when you understand how the tool works under the hood. With the above explanation of how Nuxt works under the hood, it will be easy to understand. Let's explore the benefits of using Nuxt for your next project.
Benefits of Nuxt#
Next, let's talk about the benefits of the Nuxt framework and why it's become so popular.
Create universal apps without the hassle#
Nuxt makes creating server-side rendering applications very easy. If you have ever tried to develop a server-side-rendered application with Vue.js, you likely jumped difficult hurdles due to the myriad configuration options available for both server-side and client-side.
The SSR feature is already built into Nuxt and is easy to use. You can check import.meta.server and import.meta.client in your code to decide if you're rendering something on the client-side or server-side.
It also provides the useFetch and useAsyncData composables dedicated to fetching data on the server-side of your page component.
Statically render Vue apps with universal benefit#
Static-generated websites are on the rise in the web industry, and with a single nuxt generate command, you can generate a static version of your website, with all the HTML with corresponding routes.
The nuxt generate command works exactly like the universal application approach with all the SSR features enabled but for a static generated website.
In Nuxt, a statically generated website is like building a powerful universal application without a server to power the SSR feature.
Get automatic code-splitting#
One of the reasons why Nuxt is very fast in performance is because of the code splitting feature, where a route is given its own JavaScript file with only the code needed to run that route, therefore, reducing your application size.
This code splitting feature comes from Vite, which Nuxt uses to build your app, so it works without any configuration.
Hot module replacement#
Hot module replacement (HMR) is a great feature added to resolve the time-consuming change-refresh method of software development.
When you run the development server and change a .vue file, Vite updates only the module you changed in the browser, without reloading the page or losing its state. This approach saves developers time and encourages faster application development.
Use TypeScript and modern JavaScript#
Nuxt comes with TypeScript support built in, so you can write your pages, components, and server routes in TypeScript without setting up a compiler. Vite also transforms modern JavaScript into code that runs in the browsers you support.
This feature fixes the battle of creating browser-compatible applications manually and setting up the configuration from the ground up.
Auto-imports#
Nuxt automatically imports your components, your composables, and Vue APIs like ref and computed, so you don't need to write import statements for them. You'll see this in the examples below, which use functions like useRoute() and useState() without importing them.
How to create a Nuxt app#
This section will explore how to create a Nuxt application. Before we dive in, let's explore some of the few critical concepts in developing a Nuxt application.
Creating a Nuxt application#
Creating a new project is easy and straight to the point. Make sure you have a recent LTS version of Node.js installed, such as Node.js 22 or 24.
Next, type in the following command in the folder you want to place your project:
npm create nuxt@latest <project-name>
cd <project-name>
npm run devMake sure to replace <project-name> with your actual project name. The installer asks a few questions, such as which template and package manager to use, and then the development server starts at http://localhost:3000.
Understanding Nuxt folder structure#
A new project from the minimal template only contains the app/app.vue, nuxt.config.ts, and public files. As your application grows, you add the other directories Nuxt knows about, and its structure looks like this:
my-nuxt-app/
├── app/
│ ├── assets/
│ ├── components/
│ ├── composables/
│ ├── layouts/
│ ├── middleware/
│ ├── pages/
│ ├── plugins/
│ └── app.vue
├── public/
├── server/
├── nuxt.config.ts
└── package.jsonThese files and folders are vital, as Nuxt looks for them by name and requires that they keep those names without additional configuration.
We will look at the folder structure, discuss each file and folder, and know their importance.
1. .nuxt
The .nuxt directory is hidden and generated when you start your development server, and it's called the build directory. It contains generated files such as the TypeScript types for your project, while the output of the npm run build command goes to the .output directory.
You can look at the files in this directory for debugging purposes, but don't edit them, because the files will be auto-generated again once you run the dev or build command.
2. App
The app directory holds your application code, and every directory below except public and server lives inside it. The app.vue file is the root component of your application.
3. Assets
The assets folder contains all your uncompiled assets such as images, fonts, and CSS or Sass files. Vite will process any file from this directory that you import in your application.
4. Components
The components directory is similar to the components directory in Vue.js, where all your Vue components are stored. Components are the files that make up the different parts of your pages and can be reused in your pages, layouts, and other components too. Nuxt imports them automatically, so you can use a component by its file name.
5. Composables
The composables directory holds reusable functions that use Vue's Composition API, such as a function that returns the logged-in user. Like components, they're imported automatically.
6. Layouts
The layouts folder stores the layout of your application and is very useful when separating different structures of your application for either authenticated users or guest users.
You can create many layouts to correspond to the structure of your application. Sometimes, you might want some pages on your website to have different sidebars, headers, footers, etc. These and more are structured using the layout files stored in the layouts folder. The default.vue layout applies to every page that doesn't ask for another one, as long as app.vue wraps your pages in a <NuxtLayout> component.
7. Middleware
Middlewares are custom functions that can be executed before navigating to a page, and they can be defined and stored in the middleware folder in Nuxt.
Middlewares are very handy when creating a membership-only website, and if you want to restrict access to certain pages, you can set up middleware to check if a user is logged in or not:
export default defineNuxtRouteMiddleware((to) => {
const user = useState('user', () => ({ loggedIn: false }))
if (!user.value.loggedIn) {
return navigateTo('/login')
}
})In Nuxt, middlewares work similarly to middlewares in any backend programming frameworks such as ExpressJS, Laravel, etc. It has three different types of route middleware: anonymous (or inline) middleware defined directly in a page, named middleware stored in the middleware folder that a page asks for by name, and global middleware that runs on every route change.
8. Pages
The pages directory is the bedrock of the Nuxt routing system as it reads all the .vue files inside this directory and creates the router configuration automatically.
The pages directory contains your application views and routes, and every page component is also a standard Vue component.
Still, Nuxt treats it as a route by adding special functions, like definePageMeta, to make the development of your universal application as easy as possible.
9. Plugins
The plugins directory contains JavaScript or TypeScript code you want to execute when your Vue.js application is created. This is the place to add Vue plugins and provide helpers or constants to the rest of your app.
In Nuxt, instead of calling Vue.use() on your app, you create a new file in the plugins folder and register your Vue plugin on nuxtApp.vueApp. Nuxt loads the files in this folder automatically, so you don't need to list them in nuxt.config.ts:
import MyPlugin from 'my-plugin'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(MyPlugin)
})10. Public
The public folder contains all the static files in your project that will likely not change or that should be served without any compilation on it.
All the files included in the public directory will automatically be served by Nuxt and accessible through your project root URL.
This directory is excellent for files such as favicon, robots.txt, etc.
11. Server
The server directory holds your backend code, which Nitro runs. Files in server/api become API endpoints, so a server/api/posts.ts file is served at /api/posts:
export default defineEventHandler(() => {
return [
{ id: 1, title: 'Hello Nuxt' },
{ id: 2, title: 'Server routes are neat' },
]
})A page can then fetch that data on the server with useFetch:
<script setup lang="ts">
const { data: posts } = await useFetch('/api/posts')
</script>
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>Nuxt pages and routing system#
Nuxt makes the routing system as easy as creating directories and files in the pages directory. It automatically generates the routes based on the files and folder structure of that directory.
For instance, if you have an about.vue file in the pages directory, it will automatically convert the route, and you can then visit the route on your browser to see the contents of the "About" page.
To display your pages, app.vue needs to render the <NuxtPage /> component, wrapped in <NuxtLayout> if you use layouts:
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>This routing system also allows you to define three different routes by only creating files and folders. Let's explore these route types in more detail.
1. Basic routing
Basic routing is those routes that do not require any additional configurations for them to work. It's the simplest type of routing and can be as plain as /about, /contact, etc.
To create a basic routing system, you can structure your pages directory like the below:
pages/
--| services.vue
--| contact.vue
--| index.vueNuxt will generate routes using the files you added in the pages directory automatically:
[
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'services',
path: '/services',
component: 'pages/services.vue'
},
{
name: 'contact',
path: '/contact',
component: 'pages/contact.vue'
},
]Now all our files are neatly structured and automatically routed.
2. Nested routing
Nested routes are routes that are created within a parent route. Nested routes are used to create multiple levels of deep routing.
To create nested routes, create a parent folder and place all the route files within that folder.
Let's try out creating a nested route:
pages/
--| dashboard/
-----| user.vue
-----| settings.vue
--| dashboard.vue
--| services.vue
--| contact.vue
--| index.vueIn the folder structure above, we created a new file and folder with the same name as the dashboard, and next, we added a user.vue and settings.vue files as the children of the dashboard folder.
The parent page, dashboard.vue, then renders its child pages with a <NuxtPage /> component:
<template>
<div>
<h1>Dashboard</h1>
<NuxtPage />
</div>
</template>This simple folder structure will generate routes similar to the ones below:
[
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'services',
path: '/services',
component: 'pages/services.vue'
},
{
name: 'contact',
path: '/contact',
component: 'pages/contact.vue'
},
{
name: 'dashboard',
path: '/dashboard',
component: 'pages/dashboard.vue',
children: [
{
name: 'dashboard-user',
path: 'user',
component: 'pages/dashboard/user.vue'
},
{
name: 'dashboard-settings',
path: 'settings',
component: 'pages/dashboard/settings.vue'
}
]
}
]Nested routes are a bit cumbersome to create in Vue.js especially if you have to create plenty of it as you can see in the code sample above, but Nuxt made it simple and easy by only creating Vue files in a nested folder.
3. Dynamic routing
Dynamic routes are created with unknown routes either because it depends on an API call or you don't want to create the page repeatedly. These are routes that are defined from a variable either a name, number or ID retrieved from client data on the app.
This is useful when building a news app for example, where you don't know the ID or slug of the post the user is going to click on to read. But with the dynamic route, you can retrieve the ID/slug of the post and render the correct post with the ID/slug.
To create a dynamic route, you wrap the name of the .vue file or directory in square brackets. You can name the file or directory any name of your choice, but the square brackets make it dynamic.
For instance, if you define a [slug].vue file in the pages directory, you can access the value with useRoute().params.slug. We'll demonstrate with an example how to create a dynamic route:
pages/
--| user/
-----| index.vue
-----| about.vue
-----| [routeName]/
-------| index.vue
-------| info.vue
--| dashboard/
-----| user.vue
-----| settings.vue
--| dashboard.vue
--| services.vue
--| [id].vue
--| contact.vue
--| index.vueWrapping id and routeName in square brackets will create a dynamic route for the page with ID param, along with a parent route with a string param with the above children routes. This page structure will generate the following routes:
[
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'services',
path: '/services',
component: 'pages/services.vue'
},
{
name: 'contact',
path: '/contact',
component: 'pages/contact.vue'
},
{
name: 'id',
path: '/:id()',
component: 'pages/[id].vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-about',
path: '/user/about',
component: 'pages/user/about.vue'
},
{
name: 'user-routeName',
path: '/user/:routeName()',
component: 'pages/user/[routeName]/index.vue'
},
{
name: 'user-routeName-info',
path: '/user/:routeName()/info',
component: 'pages/user/[routeName]/info.vue'
},
{
name: 'dashboard',
path: '/dashboard',
component: 'pages/dashboard.vue',
children: [
{
name: 'dashboard-user',
path: 'user',
component: 'pages/dashboard/user.vue'
},
{
name: 'dashboard-settings',
path: 'settings',
component: 'pages/dashboard/settings.vue'
}
]
}
]Inside [id].vue, you can read the parameter with useRoute(), and use validate to only accept numeric IDs. Visiting /123 renders the page, while /abc returns a 404 page:
<script setup lang="ts">
definePageMeta({
validate: (route) => /^\d+$/.test(route.params.id as string),
})
const route = useRoute()
</script>
<template>
<h1>Item {{ route.params.id }}</h1>
</template>Now that we've explored the different routing systems that come built into the Nuxt framework, let's learn how to manage state.
Managing state in Nuxt#
Nuxt 2 came with a built-in Vuex store. Nuxt 3 and 4 dropped it, and offer two options instead: the built-in useState composable for simple shared state, and Pinia, the official state management library for Vue, for larger applications.
useState creates a piece of state that's shared across your components and safe to use with server-side rendering. A value set on the server is sent to the browser with the page:
const counter = useState('counter', () => 0)
counter.value++For larger applications, install the Pinia module with this command:
npx nuxt module add piniaThen create a store in the app/stores directory. Your app/stores/users.ts file should contain the following structure, with the state, getters, and actions of your store:
export const useUsersStore = defineStore('users', {
state: () => ({
users: [] as { id: number; name: string }[],
}),
getters: {
count: (state) => state.users.length,
},
actions: {
addUser(name: string) {
this.users.push({ id: this.users.length + 1, name })
},
},
})Nuxt imports your stores automatically, so you can use one in any component by calling its function:
const store = useUsersStore()
store.addUser('Ada')Pinia also suits the split-by-feature approach in your large-scale application. With this approach, you create different store files to correspond to the features you have in your applications. For instance, if your application has users, posts, and comments features, you can create these different files such as users.ts, posts.ts, and comments.ts in your stores directory.
This method makes it easy to access a particular store file based on the feature of the application, transforming the tedious process of locating and updating data into an effortless walk in the park.
Summary#
Nuxt is a very popular full-stack Vue framework for server-rendered, statically generated, and hybrid applications. It doesn't only solve the problem of configuring and setting up SSR in Vue applications — it also boosts the development of enterprise applications by adhering to best practices in structuring and architecting large-scale Vue applications.
This guide explored everything you need to know about Nuxt 4, including what you can build with it. We discussed static-generated pages, single-page applications (SPAs), universal and hybrid applications, along with how Nuxt renders a page, its folder structure, routing, and state management.
You've now seen for yourself just how easy and beneficial it can be to choose Nuxt for your next large enterprise project.