
Understanding Nuxt 3 - Part I
Roxi
Front-end Developer
Reading time: 7 min
Updated: Jul 2, 2026
Key takeaways
- Nuxt 4 is now the current stable release and Nuxt 3 has entered maintenance mode, but the Nuxt 3 concepts in this guide still apply because Nuxt 4 builds on them.
- Nuxt 3 aligns with Vue 3, so you get the Composition API, composables, and built-in helpers like useFetch, useState, and useHead.
- The Composition API groups related logic with hooks and enables clean code reuse through composables, without replacing the Options API.
- Vue 3 offers two reactivity functions: use ref() for primitives and reassignable values, and reactive() for objects.
- Nuxt 3 introduces a new directory structure with auto-imports for components, composables, and utilities, so you write less boilerplate.
Why Nuxt 3 still matters in 2026
In web development, you can't learn one tech stack and coast to seniority. You have to keep learning. Technical debt is a real part of the job. In your first years of coding, that may not feel obvious. With experience, though, the pattern becomes clear. Prioritising speed over clean code has a cost. That cost for future reworking will show up sooner or later.
So, as cliché as it sounds, becoming a strong developer is a journey, not a destination. The first years of learning a language can feel like a burden. Yet as your skills grow, you learn to write neat, maintainable code. More importantly, you learn to adapt to change. That, in my opinion, is the key skill for a career in web development.
Unless you code in COBOL, your language will change. Expect bug fixes, performance and feature improvements, community polish, and deprecations. So how do I like to think about writing maintainable code?
Write code as if you'll need to refactor it in two years.
With that mindset, let's dive into Nuxt 3 and see how the Composition API differs from the older Options API used in Nuxt 2. A quick note on versions first. Nuxt 4 is now the current stable release, and Nuxt 3 entered maintenance mode in mid-2026. The good news is that almost everything in this guide still applies. Nuxt 4 builds directly on the Nuxt 3 foundations, so the concepts below carry forward. For the latest version details, always check the official Nuxt documentation.
Nuxt 3 vs Nuxt 2: what changed
Let's look at some of Nuxt 3's additions and what makes them different from Nuxt 2.
- Nuxt 3 is aligned with Vue 3. You can use all the great Vue 3 features, such as the Composition API and composables. Nuxt ships built-in composables too, like useFetch(), useState(), and useHead().
- It uses the h3 web server, an upgrade from the connect server in Nuxt 2.
- It uses Vite for bundling and can support webpack via a plugin.
- It offers flexible dynamic pages. You can make just part of a file name dynamic with the new bracket syntax that replaces the old underscore syntax.
- It handles metadata and SEO through composables.
- It runs on Nitro, a server and serverless engine.
- Nuxi is the Nuxt CLI. It gives you a zero-dependency way to scaffold projects and add modules.
- It comes with a new directory structure and no routing by default.
- It has a much smaller bundle size on both client and server.
- It's fast. Bundling and hot reloading feel almost instant, thanks to Vite.
- It introduces hybrid rendering, so you can choose per page between server-side rendering and static rendering with caching.
- It's SEO friendly out of the box.
Next, let's break down some of the most notable features so these new terms make sense.
1. The Composition API
Until Vue 3, there was one official way to build a Vue component: the Options API. Logic was split into separate parts like data, methods, computed, and watchers. With the Composition API, you can do all of that in a more organised way. The result is cleaner code that's easier to manage.
The main advantage of the Composition API is more efficient logic reuse. It solves the drawbacks of mixins. It also removes the need to scatter code across different sections of a file.
Let's look at how much code sits in a folder explorer component from Vue CLI's GUI. The Composition API looks far more organised. You don't have to jump around the file to work out what goes where.

Source: Vue.js Composition API FAQ
In a nutshell, the Composition API solves two big limitations of the Options API.
- It groups related pieces of code together using hooks.
- It makes it easy to reuse code across your app with composables.
Vue composables are functions that use the Composition API to build reactive, reusable logic. A composable acts as an external function. It extracts reactive state and behaviour so you can share it across components. This is similar to Options API mixins.
Composables can also be nested. One composable can call one or more others. That lets you break complex logic into smaller units, much like you split an app into components.
The Composition API in Nuxt 3 does not replace the Options API. Developers can use either API or both, based on their needs and preferences.
The <script setup> tag defines a component's reactive state, computed properties, and functions using Composition API syntax. The plain <script> tag tells Nuxt you're using the Options API. Let's see a basic example of both working together. Notice that we define the hello message with a ref() function instead of data(). This is the new way to declare reactivity in Vue 3, so let's explore the new Reactivity API next.

2. Reactivity with the Composition API
In Vue 2, we used the data() function to declare all variables. In Vue 3, we have two functions for reactive data: ref() and reactive(). The key difference is simple. The ref() function declares reactive state for primitives and objects. The reactive() function only declares reactive state for objects. Let's look at some example code.
Ref()

The ref() method takes a single value and returns a mutable, reactive object. To read the tracked value, you access the value property. When refs are used as top-level properties in the template, Vue unwraps them automatically. So you don't need .value there.
Reactive()

Unlike ref(), reactive() only works with an object. Each property of that object can be its own reactive variable. You also don't need an intermediary .value property to read or update it. You can access the object's properties directly.
So when should you choose one over the other?
Use ref() for primitives, always. Keep in mind that ref() also suits objects that need to be reassigned, such as an array. With reactive([]), Vue tracks mutations of that array. But you can't swap it for a different array.
3. The Nuxt 3 directory structure
A nice Nuxt 3 feature is auto-import for Vue APIs like ref() and reactive(). This speeds up how you write code. It means the examples above would work without the import statement under <script setup>.
Auto-import also works for components, composables, and helper functions by default. Every exported function and variable in the components/, composables/, and utils/ folders is available in any component. No explicit import needed.
Want to disable auto-imports? Set imports.autoImport to false in your nuxt.config.ts.
Now let's look at this diagram and walk through the skeleton of a Nuxt 3 app.

Source: Krutie Patel on Twitter
- An app.vue file is added. It's the main component of your app. Anything you put in it, such as CSS or JS, is global and included on every page.
- The pages/ directory is optional. You can build an app with just app.vue and components in components/. In that case, vue-router isn't used and the build is much lighter.
- A new composables/ directory is added. Each composable here is auto-imported, so you can use it directly.
- The .output/ folder holds all build files after nuxt build. The .nuxt/ directory holds everything needed to generate your app. Don't edit files in either one.
- The assets/ folder holds assets the build tools process, usually stylesheets, fonts, and images that the server won't serve directly.
- The layouts/ folder works as it did in Nuxt 2. It extracts common UI patterns into reusable layout components.
- The middleware/ folder holds functions that run before you visit a given route.
- The public/ folder was the static folder in Nuxt 2. It's served at the server root and holds files that keep their names, like robots.txt, or rarely change, like favicon.ico.
- The plugins/ folder adds app-level functionality. Nuxt reads these files and loads them when the Vue app is created.
- The modules/ folder provides a module system that extends the core. Modules are functions called in sequence when Nuxt boots.
- The utils/ directory auto-imports helper functions across your app.
- Inside the server/ folder, the ~/server/api, ~/server/routes, and ~/server/middleware directories register API and server handlers with HMR support. Like frameworks such as Express, keeping server and client code in one codebase is convenient. It saves you from maintaining a separate API project.
Wrapping up
In this first part, we covered the major differences between Nuxt 3 and Nuxt 2 and toured the new folder structure. We saw what the Composition API looks like and learned new ways to declare reactive data. My advice is to read the official Nuxt 3 and Vue 3 docs and play with these concepts on StackBlitz, the online IDE you'll see in the docs.
Why get a firm grip on these fundamentals? Because they set the base for the next chapter: state management. In Understanding Nuxt 3 - Part II, we'll see how ref() and reactive() help manage state across components. We'll also build a Nuxt 3 app from scratch.
Want a team that ships production-grade Nuxt and Vue apps? Explore our Nuxt.js development and Vue.js development services, or see how our web development team works. Until then, thanks for reading and happy coding.



