Nuxt 3 Migration Simplified: A Cheat Sheet

Introduction

Upgrading to Nuxt 3 from Nuxt 2 can be a little intimidating.

There are a ton of changes to keep track of, and it's easy to feel like you're lost in a maze of code.

But don't worry, I'm here to help!

I've started this cheat sheet to simplify the upgrade process for myself and for others who are brave enough to tackle it.

It's a work in progress, and not intended to be exhaustive. I'll be updating it as I migrate more sites.

Just remember that the migration process will take some time, patience, and persistence, depending on the size of your project and your prior experience.

But hey, at least you'll learn a bunch.

And if you're just looking to upgrade some modules, I've written an upgrade guide for 23 of them.

Let's do this!

Resources

Docs

Tech

  • UnJS – A collection of packages which make up the Nuxt core.
  • VueUse – Essential Vue Composition API utilities.
  • Vite.js and Rollup – Backbone of Nuxt 3 bundling.

Nuxt 3 Examples

Education

Getting help

Need quicker help? Try providing a reproduction repo.

At a minimal, provide the output of npx nuxi info.

Pre-migration

Before you start upgrading, it's important to familiarise yourself with the new features and changes in Nuxt 3 and prepare your project for the upgrade.

1. Get familiar with Nuxt 3

Learn the key differences between Nuxt 2 and Nuxt 3 and the new technologies that power it.

While there are many similarities to Nuxt 2, the underlying architecture is completely new, more complex and more powerful.

Start by learning the differences between:

  • Nuxt (core context)
  • Nuxt App (app context)
  • Nitro (server engine)
  • Nuxi (command line interface)

You may want to re-familiarise yourself with the Rendering modes (SSR, SPA, Static).

Daniel Roe's talk What happens when you start Nuxt 3 is a great place to start.

Having a rough understanding of the migration process will allow you avoid common pitfalls.

Keep this open as you'll reference this as you continue on with the migration process.

Create a new fresh Nuxt 3 project using nuxt.new, play around with it.

This will help you get a feel for the new features and how they work.

Vue 3 is a major upgrade from Vue 2. You will need to learn the new syntax and features to be able to use Nuxt 3, particularly the Composition API.

Whether you like TypeScript or not is irrelevant. Nuxt 3 is written in TypeScript, trying to avoid it will only make your life harder.

You don't need to be an expert, but you should be able to read and understand the Nuxt 3 code.

You can learn the basics with TypeScript in 5 minutes.

Nuxt 3 requires ECMAScript modules, so you'll need to convert your CommonJS modules. See the Nuxt ES modules page.

Many of the issues you'll come across are due to using dependencies which are CJS. So understanding the difference between them can be critical.

Node modules can be a challenging subject, if you need further guidance see this article.

2. Prepare your Nuxt 2 app

Before you start migrating, you should prepare your Nuxt 2 app to make the upgrade process easier.

If you are on an earlier version, the first step is upgrading.

The latest Nuxt 2 uses Vue 2.7, which supports the Composition API.

npm i nuxt@^2.15.4
# yarn add nuxt@^2.15.4

Upgrade your modules to the latest version that supports Nuxt 2. This will make the upgrade process easier.

Be careful not to upgrade modules which may only support Nuxt 3 in future versions.

Nuxt 3 has auto-imports enabled by default, if you're not using this feature in Nuxt 2, you should enable it to identify any component paths that need to be updated.

All manual component imports should be removed.

See Components Discovery for more information.

nuxt.config.ts
export default {
  components: true
}

Having hard dependencies in your project that aren't compatible with Nuxt 3 / Vue 3 is going to massively slow you down.

You should go through all your node dependencies, checking the following:

This is a good opportunity to drop any dependencies you don't need, migrate to dependencies which have better maintenance and / or are smaller.

Migration

This cheat sheet requires a fresh Nuxt 3 project and migrating things over one by one.

In each step of migrating, you should test to see if everything is working. If there are bugs, then you will be able to debug them easily.

1. Create the new app

The first step is creating the new apps boilerplate and migrate over safe config from nuxt.config.ts.

You can do so with npx nuxi init my-app.

This will create a new Nuxt 3 project.

By default, Nuxt 3 does not provide page routing and layouts. To make the migration easier, you'll be modifying the files to be Nuxt 2 compatible.

To enable them we update app.vue with NuxtLayout and NuxtPage.

app.vue
<template>
  <div>
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </div>
</template>

Create an empty default layout file that will be updated later.

layouts/default.vue
<template>
  <div>
    <slot />
  </div>
</template>

Create a basic index page, so we can see our changes.

pages/index.vue
<template>
  <div>
    <h1>hello world</h1>
  </div>
</template>

Follow the Migrate Runtime Config doc.

The env key in nuxt.config.ts is no longer supported, you should use runtime config exclusively.

// Nuxt 2
export default {
  privateRuntimeConfig: {
    apiKey: process.env.NUXT_API_KEY || 'super-secret-key'
  },
  publicRuntimeConfig: {
    websiteURL: 'https://public-data.com'
  },
  env: {
    NUXT_API_KEY: process.env.NUXT_API_KEY
  }
}
// Nuxt 3
export default defineNuxtConfig({
  runtimeConfig: {
    apiKey: process.env.NUXT_API_KEY || 'super-secret-key',
    // Warning: `public` is exposed on client and server
    public: {
      websiteURL: 'https://public-data.com'
    }
  }
})

You can safely ignore TypeScript issues this causes for now.

If you previously had head configuration in your nuxt.config.ts, you should migrate it to app.head config.

If you're using hid in your head config, unless you have a good reason, you should remove it. Read @vueuse/head v1 release notes to learn more.

Note: Tags with href and src will not resolve relative links / aliases. You will need to reference absolute links from the public directory. If you'd like to use aliases, you can use the nuxt-unhead module.

// nuxt 2
head: {
  meta: [
    { hid: 'description', name: 'description', content: 'My custom description' },
  ]
}
// nuxt 3
app: {
  head: {
    meta: [
      { name: 'description', content: 'My custom description' }
    ]
  }
}

In Nuxt 3 the /static folder has been renamed to /public. Make the public directory and copy all of these files over.

In Nuxt 3, the assets folder serves the same function. Simply copy over your assets' folder.

Once done, update the css field.

nuxt.config.ts
css: [
  '~/assets/main.css',
]

Most pre-processors will work with no extra config.

You will just need to install the dependencies. For example, if you see an error like:

Preprocessor dependency "sass" not found. Did you install it?

You will need to run npm install -D sass.

If you're having issues with linked images within CSS, you may need to use absolute links to the images in the public directory.

/* Nuxt 2 - we have the file: assets/images/logo.png */
body {
  background-image: url('~/assets/images/logo.png');
}
/* Nuxt 3 - we have the file: public/images/logo.png */
body {
    background-image: url('/images/logo.png');
}

You may have some node_module dependencies in your css entries, install each of these as needed.

2. Modules

Module migration can be a challenging part of the migration process. You will need to migrate each module one by one and test your app after each module.

Nuxt 2 modules are not compatible with Nuxt 3 out of the box, so many modules are not supported yet. I have tried to provide an upgrade path for the most popular modules which are not supported.

In Nuxt 3 all modules should belong under the modules key. You will need to copy over your modules from the buildModules and modules fields.

nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    // your modules
  ]
})

@nuxtjs/axios and @nuxt/http are not recommended with Nuxt 3.

It's best practice to use the $fetch / useFetch API.

If are determined to use $http you can use nuxt-alt/http.

See the Nuxt 3 and Axios discussion for additional context.

@nuxtjs/auth

Official @nuxt/auth support is a work-in-progress.

In the meantime, you can use:

@nuxtjs/pwa

The community nuxt-pwa-module module seems to be the best option.

A core module for PWA seems to be undecided, if it goes ahead there's no ETA.

@nuxtjs/i18n

Official support is in beta, check the docs.

@nuxtjs/proxy

This feature is coming to nitro route rules. You can track it here: https://github.com/unjs/nitro/issues/113.

In the meantime, you can use the nuxt-alt/proxy module.

@nuxtjs/eslint-module

The Nuxt 2 version of this module is called @nuxtjs/eslint-config, you should remove this.

Instead, follow the documentation for @nuxt/eslint-config.

@nuxtjs/sitemap

Not currently supporting Nuxt 3.

Instead, you can use:

@nuxtjs/robots

Not currently supporting Nuxt 3.

Instead, you can use:

Alternatively, create your own robots.txt file at public/robots.txt.

public/robots.txt
User-agent: *
Disallow: /

OR you can generate robots dynamically.

server/routes/robots.txt.ts
export default defineEventHandler(
  e => `User-agent: *\nDisallow: /\nSitemap: https://YOUR_SITE/sitemap_index.xml`
)

@nuxtjs/vuetify

Official support is coming soon. You can use nuxt-alt/vuetify in the meantime.

@nuxtjs/device

Not supporting Nuxt 3.

Create your own plugin to detect the device instead, you can use the ua-parser-js package directly.

composables/useDevice.ts
import UAParser from 'ua-parser-js'

export function useDevice() {
  const parser = new UAParser()
  const { os, browser, device } = parser.getResult()

  return {
    os,
    browser,
    device,
    isMobile: device.type === 'mobile',
    isDesktop: device.type === 'desktop',
    isTablet: device.type === 'tablet',
    // etc
  }
}
pages/index.vue
<script lang="ts" setup>
const { isDesktop, browser } = useDevice()
</script>

@nuxtjs/moment

Not supporting Nuxt 3.

Create your own plugin to use Moment instead.

plugins/moment.ts
import moment from 'moment'
import 'moment/locale/en'

export default defineNuxtPlugin((nuxtApp) => {
  moment.locale('en')
  moment.tz.setDefault('UTC')
  return {
    provide: {
      moment,
    }
  }
})
pages/index.vue
<script setup lang="ts">
// access moment via $moment
const { $moment } = useNuxtApp()
</script>

@nuxtjs/fontawesome

I'd recommend using nuxt-icon.

Alternatively, see the Using FontAwesome in Nuxt 3 discussion for plugin code sample.

@nuxtjs/dayjs

See Nuxt3 support for plugin code sample.

@nuxtjs/toast

See Examples of using the @nuxtjs/toast or any vue toast plugin? discussion for plugin code sample.

@nuxtjs/gtm

You can create your own code to handle gtm.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          innerHTML: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','YOUR_GTM_ID');`
        },
      ],
    },
  }
})

bootstrap-vue

See nuxt3 starter? issue for code sample.

Work in progress.

@nuxtjs/google-analytics

See Vue Plugins from the docs for an example of how to use Google Analytics with Nuxt 3.

Alternatively, see this comment on how you can implement it with cookie control.

If the module you're using supports Nuxt 3, it's important to check the migration notes. It's likely that the upgrade will include some breaking changes.

3. State management

If you're using Vuex 3, you will need to upgrade your state management, as Vue 3 does not support it.

Nuxt 3 ships with its own statement management with the useState composable.

If you're using a state management library, you should consider if you need it.

Additionally, if you are dealing with data client side, you can always reference global refs. This isn't recommended for server-side rendering as state is shared between users.

composables/useCounter.client.ts
import { ref } from 'vue'

// You can import this counter from any file and the data will stay in sync
export const counter = ref(0)

export function useCounter() {
  return {
    counter,
    increment() {
      counter.value++
    },
    decrement() {
      counter.value--
    }
  }
}

Pinia is the recommended state management solution for Nuxt 3, but you can use Vuex 4 if you prefer.

4. Components

Components will only be parsed if they are imported. This means it's safe to copy over all our components and mixin files from Nuxt 2.

The folders have the same name and will be auto-imported. You don't need to add anything to nuxt.config.ts.

If your components aren't being found, it may be because component auto-imports are done with path-prefixing by default, see Component Names.

See the Component Options Migration guide for more information.

5. Pages and dependencies

You will be copying over your pages one by one and their dependencies.

As you copy each page, you will run into errors. These errors may be related to breaking changes between Nuxt 2 and 3 or to missing dependencies.

For example, you'll have missing layout, plugin and potentially component dependencies.

It's important you reference the Nuxt 3 Migration Guide as you go. Remember to ask for help in the community channels if you get stuck.

Start with the home page, pages/index.vue. This will give you some momentum and help you get started.

You'll start with routes which aren't dynamic which tend to be less complex and easier.

For example: pages/about.vue, pages/contact.vue, etc.

Nuxt 3 uses a new file system routing system, which means that all our pages need to be converted to the new format.

See Dynamic Routes for more information.

6. Server Routes / Middleware

Nuxt 3 uses a completely new server engine and HTTP framework. This means that all our server routes and middleware need to be converted to the new format.

See Migrate Server Routes.

You'll likely need to completely rewrite your server routes.

See Migrate Server Routes.

You'll likely need to completely rewrite your server middleware.

Post Migration

1. Recommendations

While this is not required, it is highly recommended. The Composition API is a much better way to write Vue code and Nuxt provides better support for it.

Tips:

Nuxt 3 is TypeScript first, it is the best practice to have all of your code in TypeScript.

If you're using the Options API, you can simply add lang="ts" to your Vue SFCs. Otherwise, if you are using the options API, you can make use of the defineNuxtComponent function.

components/MyComponent.vue
<script lang="ts">
export default defineNuxtComponent({
  props: {
    name: {
      type: String,
      required: true
    }
  },
  mounted() {
    console.log(this.name)
  }
})
</script>

In Nuxt 2 it was common to load everything from ~/assets, however with Nuxt 3 the default is to use absolute paths.

This way the bundler does not need to parse them. This will save you time when migrating and improve your build performance.

Simply move images which aren't going to change to static and load them from / instead of ~/assets.

To make the migration from Vue 2 as simple as possible, you can make use of vue-macros.

It provides a number of useful build-time macros, which speeds up the time to convert the code.

components/MyComponent.vue
<script setup lang="ts">
const { modelValue, count } = defineModel<{
  modelValue: string
  count: number
}>()

console.log(modelValue.value)
modelValue.value = 'newValue'
</script>

The VueUse and UnJS packages all support Nuxt 2 and Nuxt 3. If you can migrate to using them over other dependencies, you'll save yourself a lot of time.

2. Testing

Testing in Nuxt 3 is still under development. There is some documentation around it, but it is fairly minimal.

Nuxt 3 uses Vitest for testing. If you're using Jest or Mocha, you'll need to migrate.

See the Why Vitest guide for more information.

Follow the documentation on the Testing page, to get started.

The vitest-environment-nuxt package will likely take over as the official testing environment for Nuxt 3.

It's still a work-in-progress with minimal documentation, but you may consider using it.

Conclusion

You likely still have some migration pain ahead of you, but you're well on your way, congratulations.

Feel free to reach out to me directly on Twitter or Discord with any feedback or questions you may have. Keep in mind that this migration guide does not intend to be exhaustive and is being actively updated.

This post took me many hours to put together. Please consider sponsoring me if this has helped you.