Website loading

Understanding Nuxt 3 - Part I

Front-end Developer
Apr 27, 2023 โ€ข 7 min

Why learn it, what’s new and why it matters?

In the world of web development, one cannot just learn one tech stack and climb his way to seniority without constantly learning. The concept of technical debt is a serious part of the job. Although one might not emphasise this in the first years of coding, the more experience you get, the easier it is to see that prioritising speedy delivery over perfect code is something that can easily lead to disastrous effects (the cost of future reworking will show up, in one way or another).

So, as cliché as it sounds, gaining experience as a developer is a journey, not a destination. While the first year(s) of learning a new programming language might seem a burden, once you'll develop your skills, you will learn to write your code as neatly as possible. And more than that, you will learn to adapt to changes, which in my opinion, is the key factor for working in the web development industry.

Unless you code in COBOL ๐Ÿ˜…, your programming language will suffer changes. I’m talking about bug fixes, performance/feature improvements, polishes brought by contributors, and deprecations. So in terms of writing maintainable code, how do I like to think about it?

Write code as if you will need to refactor it in 2 years’ time.

With this in mind, let’s dive into the highly awaited release of Nuxt 3 and see how different Composition API is versus the former Options API used in Nuxt 2 ๐Ÿ‘ฉ‍๐Ÿ’ป. 

Nuxt 3 vs Nuxt 2

Let's take a look at some of Nuxt 3’s new additions and what makes them different from Nuxt 2:

  • Nuxt 3 version is aligned with Vue 3, so you can leverage all the great features of Vue 3, such as Composition API, composables and more. Nuxt already offers some of its functionality in the form of built-in composables like useFetch(), useState(), and useMeta().
  • Nuxt 3 uses an h3 web server which is an upgrade from the connect web server in Nuxt 2.
  • Nuxt 3 uses Vite for bundling and can also support webpack 5 via a plugin.
  • Flexible dynamic pages, as now you can declare just a part of the file name dynamic (new brackets syntax replacing the underscore syntax).
  • Uses composable VueUse Head for metadata and SEO.
  • A Server(less) packager Nitro.
  • Nuxi is a new Nuxt CLI. It provides a zero-dependency experience for easy scaffolding of new projects and module integration.
  • Nuxt 3 comes with a new directory structure and doesn’t include routing by default.
  • Much smaller and lighter Bundle size (up to 75x), both on client and server.
  • It’s blazingly fast, so bundling and hot reloading are almost instant (Vite is the culprit).
  • Introduces Hybrid rendering to define whether your pages should be server-side rendered or statically rendered using caching.
  • It’s SEO friendly.

Next, let’s dissect some of the most notable features in order to get a better understanding of what these new terms mean:

1. Composition API

Until Vue 3, we used to have one official way to create a Vue component which was done using Options API. The whole logic was separated into multiple parts like data, methods, computed, and watchers - but now, with Composition API, you can do all those things in a more organised way, making your code much cleaner and easier to manage.

The primary advantage of Composition API is that it enables more efficient logic reuse, and it solves all the drawbacks of mixins; plus, we also get rid of writing code in different sections of the file.

Let’s look at how much code lays in the folder explorer component from Vue CLI's GUI. Composition API looks way more organised since you don’t have to jump around in the file to make sense of where’s what.


In a nutshell, Composition API solves 2 major limitations that Options API had:

  • Groups relevant pieces of code together using hooks.
  • Helps to reuse code throughout your application very easily using composables.

Vue composables are functions that use the composition API to implement a reactive and reusable logic. Composables act as an external function that extracts reactive state and functionality to be used across several other components - similar to the Options API mixins.

Another interesting thing about composables is that they can be nested within each other, meaning one composable function can call one or more other composable functions. Therefore, we are able to break down complex logic into smaller units, like how we separate an entire application using components.

The Composition API in Nuxt 3 does not exclude the Options API, and developers can choose to use either or both of these APIs based on their preferences and requirements.

Using the <script setup> tag in a component defines its reactive state, computed properties, and functions using the Composition API syntax, while using the plain <script> tag tells Nuxt you’re going for the Options API. Let’s see a basic example of how both can work together. As you can notice, we define the hello message via a ref() function instead of data() - this is the new way of declaring reactivity in Vue 3, so let’s start by exploring a bit of the new Reactivity API concepts.

2. Reactivity with Composition API

While in Vue 2, we used the data() function in order to declare all variables, with Vue 3, we now have two functions for declaring reactive data - ref() and reactive().
The most significant difference between the two is that the ref() function allows us to declare a reactive state for primitives and objects, while reactive() only declares a reactive state for objects. Let’s take a look at the following example code:


The ref() method takes a single value and returns back a mutable and reactive object. To access the value that ref() is tracking, we access the value property of the returned object. Yet when refs are accessed as top-level properties in the template, they are automatically unwrapped, so there is no need to use .value.


Unlike ref(), reactive() can only be initialised with an object. Each property of the object can be a different reactive variable, however. Also, we don't need to use an intermediary property like .value in order to get or change the properties of our reactive object, we can just call the properties of the object directly.

Ok, now one might ask when to choose one over the other.

We'll always use ref() for primitives, of course, but keep in mind that ref() is good for objects that need to be reassigned, like an array. That’s because when you write reactive([]), Vue tracks when that array is being mutated yet there's no way to change it for a different array.

3. Directory Structure

A very nice feature which Nuxt 3 brings is that it can now auto-import Vue APIs (ref(), reactive(), etc.) in order to speed up the delivery of developers’ code. So that means the examples above would work without the import statement that I placed under <script setup>.

The great news is that the auto-import also works for components, composables and helper functions by default without the need to explicitly import them! All exported functions and variables in files placed under the components/, composables/ and utils/ folders are globally available to use in any component.

Of course, in case you want to disable auto-imports for any reason, you can set imports.autoImport to false in your nuxt.config.ts.

Now let’s take a brief look at this diagram and explain 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 application. Whatever you put in it (CSS, JS, etc.) will be globally available and included on every page.
  • The use of the pages/ directory is optional. You can build your app only with app.vue as a main component and other components placed in the components/ folder. If that’s the case, vue-router won’t be used, and the app’s build will be much lighter.
  • A new composables/ directory is added. Each composable added here is auto-imported, so you can use it directly in your application.
  • The .output/ folder holds all build files when building your Nuxt application for production (nuxt build), while the .nuxt/ directory contains everything needed to generate your application and is required that you do not make changes to any files in these directories.
  • The assets/ folder holds all website's assets the build tools will process - usually your stylesheets, fonts and images that are not going to be served by the server.
  • The layouts/ folder has the same role as in Nuxt 2, used for extracting common UI or code patterns into reusable layout components.
  • The middleware/ folder holds functions that you want to run before traversing a particular route.
  • The public/ folder was previously known as the static folder in Nuxt 2. In Nuxt 3 the public/ directory is directly served at the server root and contains public files that have to keep their names (e.g. robots.txt) or likely won't change (e.g. favicon.ico).
  • The plugins/ folder contains code that usually adds app-level functionality to your Nuxt app. Nuxt automatically reads the files in your plugins directory and loads them at the creation of the Vue application.
  • The modules/ folder provides a higher-order module system that makes it possible to extend the core. Modules are functions that are called sequentially when booting Nuxt.
  • The utils/ directory imports helper functions and other utilities throughout your application automatically.
  • Inside the server/ folder, the ~/server/api, ~/server/routes, and ~/server/middleware directories are used to register API and server handlers with HMR support. As per frameworks such as Express, having the server code and client code on the same codebase makes it really convenient for the developers and eliminates the burden of maintaining another project just for creating APIs !

Wrapping Up

In the first part of this article, we listed the major differences that Nuxt 3 brought over Nuxt 2 and had a look over the new folder structure that a website app has. Now that we went through what Composition API looked like and learned the new ways of declaring reactive data, what I recommend is that you have a look at the official Nuxt 3/ Vue 3 documentation and play around with these concepts on StackBlitz (an online IDE which you’ll see in the docs).

The reason why I suggest getting a good grip on these fundamental concepts is that they lay the base for the next important chapter that will come in the second part of the article: State Management.
In “Understanding Nuxt 3 - Part II“, we will learn how ref() and reactive() can provide a way of managing state across components and have a hands-on experience in which we create a Nuxt 3 app from scratch.

Until then, thanks for reading my article and happy coding!

tech insights & news


Stay up to date with the tech solutions we build for startups, scale-ups and companies around the world. Read tech trends and news about what we do besides building apps.