Still on Nuxt 2 or Vue 2?

Nuxt 2 to Nuxt 4 Migration in 2026: Cost, Timeline, Plan

Nuxt 2 to Nuxt 4 Migration in 2026: Cost, Timeline, Plan

A Nuxt 2 to Nuxt 4 migration guide: what Vue 2 and Nuxt 2 end of life means, what breaks, cost and timeline estimates, and a step-by-step plan.

Liviu Zachin

· 9 min read

If your product still runs on Nuxt 2 or Vue 2, it probably still works. That is the problem: nothing is on fire, so the upgrade keeps sliding down the roadmap. Here is how I approach these migrations as a tech lead: the risks, the options, what breaks, and how I size and plan the move to Nuxt 4 without stopping feature work.

Where Nuxt 2 and Vue 2 stand in November 2026

  • Vue 2 reached end of life on December 31, 2023. The last release is 2.7.16.
  • Nuxt 2 reached end of life on June 30, 2024. The last release is 2.18.1.
  • Nuxt 3 reached end of life on July 31, 2026, after its original January 2026 date was extended.
  • Nuxt 4 shipped in July 2025 and is the current stable line (4.6 at the time of writing).
  • Nuxt 5 has no release yet. The official roadmap estimates Q4 2026, built on Nitro v3 and h3 v2.

So skip Nuxt 3, which is already unsupported, and go straight to Nuxt 4. Nuxt 4 is supported until six months after Nuxt 5 ships, and since 4.2 you can opt into many Nuxt 5 changes with future.compatibilityVersion: 5, as this site does.

What end of life means in practice

Security. Two Vue 2 vulnerabilities published in 2024, CVE-2024-6783 (medium-severity XSS) and CVE-2024-9506 (low-severity ReDoS), are not fixed in the public packages. In a SOC 2 audit or an enterprise security questionnaire, an unsupported framework is a finding you have to explain.

Dependencies. Vuetify 2 reached end of life in January 2025. BootstrapVue's last release was in October 2022, on Bootstrap 4, and it never shipped a Vue 3 version. Pinia 4, released in July 2026, requires Vue 3, and in October 2026 the @nuxtjs/axios and @nuxtjs/auth repositories were archived.

Hiring. Developers who learned Vue in recent years learned Vue 3, the Composition API and Vite, and a Vue 2, webpack 4 and Vuex codebase is a harder sell to them.

Your three options

1. Incremental migration

You prepare the codebase on Nuxt 2, switch the framework underneath it, then port the rest page by page. This is what I recommend in most cases: the app stays releasable and the risk arrives in small, reviewable pieces.

2. Big-bang rewrite

You start a fresh Nuxt 4 project and rebuild everything. AI coding tools make this look cheap, but a rewrite throws away years of bug fixes and undocumented edge cases, and freezes the old app while the new one catches up. I only recommend it when the app is small, the code is beyond saving, or most screens are being redesigned anyway. If you rebuild with AI tools, run through my checklist for taking a vibe-coded app to production before launch.

3. Paid extended support

HeroDevs sells Never-Ending Support (NES) for Vue 2, Nuxt 2 and Vuetify 2: drop-in packages that keep receiving security patches, built in partnership with the Vue and Nuxt core teams and listed in their end-of-life announcements. Both CVEs above are patched in NES. Pricing is quoted by the number of developers committing to the repository.

It is a fair choice if the app will be retired within two years, if an audit can't wait for a migration, or if you want time to migrate properly. It doesn't fix the ecosystem, hiring or the migration itself: you pay to stand still, then pay for the move later.

What actually breaks

Nuxt 2 / Vue 2Nuxt 4Effort
Vuex, auto-registered from store/Pinia through @pinia/nuxtOne store module at a time
Options API componentsStill supportedConvert only when you touch a file
Filters (Vue.filter)Plain functions, e.g. in app/utils/Many small template edits
Event bus ($on, $off, $once)Props and emits, provide/inject, Pinia or mittNeeds thought
Vuetify 2, BootstrapVueVuetify 3+, BootstrapVueNext or another UI libraryOften the largest item
webpack 4, build.extendVite, configured under viteCustom loaders need replacing
asyncData, fetch(), head()useAsyncData, useFetch, useSeoMetaPage by page
Plugins with inject, middleware with redirectdefineNuxtPlugin with provide, defineNuxtRouteMiddleware with navigateToMechanical
@nuxtjs/axios, @nuxtjs/auth$fetch, Nuxt Auth Utils or similarAuth needs its own tests
serverMiddlewareNitro server routes in server/apiUsually small
pages/, components/ and co. at the rootapp/pages/, app/components/; server/ and public/ stay at the rootA file move

Data fetching: asyncData to useFetch

A typical Nuxt 2 page:

<script>
export default {
  async asyncData({ params, $axios }) {
    const post = await $axios.$get(`/api/posts/${params.slug}`)
    return { post }
  },
  head() {
    return { title: this.post.title }
  },
}
</script>

The same page in Nuxt 4:

<script setup lang="ts">
const route = useRoute()
const { data: post } = await useFetch(() => `/api/posts/${route.params.slug}`)

useSeoMeta({
  title: () => post.value?.title,
})
</script>

In Nuxt 4, data is a shallow ref that starts as undefined, not null, and calls with the same key share it. useFetch is not a drop-in replacement for the Nuxt 2 fetch() hook, so I review those by hand.

Vuex to Pinia

A Nuxt 2 store module, store/cart.js:

export const state = () => ({ items: [] })

export const getters = {
  total: state => state.items.reduce((sum, item) => sum + item.price, 0),
}

export const mutations = {
  add(state, item) {
    state.items.push(item)
  },
}

The Pinia version, app/stores/cart.ts:

interface CartItem {
  id: string
  price: number
}

export const useCartStore = defineStore('cart', {
  state: () => ({ items: [] as CartItem[] }),
  getters: {
    total: state => state.items.reduce((sum, item) => sum + item.price, 0),
  },
  actions: {
    add(item: CartItem) {
      this.items.push(item)
    },
  },
})

Components go from this.$store.commit('cart/add', item) to useCartStore().add(item). Pinia 2 still supports Vue 2.7 and can run next to Vuex, so I often start moving stores before the framework switch.

UI libraries, build and modules

The UI library is where estimates go wrong. Vuetify 3 and later change components, props and the grid, and BootstrapVueNext targets Bootstrap 5. Either touches nearly every screen, so I plan it as its own stream of work.

Vite is the default builder. The friction is custom webpack loaders, require() calls and process.env reads, since Nuxt is ESM-native and runtime values go through useRuntimeConfig. I check every module in the Nuxt module directory for a Nuxt 4 version. Nuxt 4 still detects the old folder layout, but since everything moves anyway, I adopt app/ now.

How to size the job

Before quoting, I read and run the code and build an inventory:

  • Pages and layouts, each a unit of porting and testing.
  • Components, split into presentational ones and those using filters, mixins, $on or this.$store.
  • Vuex modules, with their size and how many components use them.
  • Modules and plugins, each marked "has a Nuxt 4 version", "has a replacement" or "abandoned".
  • Custom webpack config, serverMiddleware and SSR quirks, such as code that touches window at import time.

Then test coverage. Vue 2 unit tests often need migrating themselves. What carries a migration is end-to-end tests on critical flows such as sign-in, checkout and key forms. Playwright drives a real browser and doesn't care which framework rendered the page, so tests written against the Nuxt 2 app define "done" for the Nuxt 4 one. If there are none, writing them comes first.

Timeline and cost estimates

These are estimates, not quotes, at my day rate of €480. Migrations start from €3,000, and the real number comes after I have read your code.

App sizeTypical inventoryEffortCalendar timeEstimated cost
SmallUp to 20 pages, a small Vuex store, a few modules, no UI library swap6–12 days1–3 weeks€3,000–€5,800
Medium20–60 pages, 100–200 components, several Vuex modules, Vuetify 2 or BootstrapVue15–30 days4–8 weeks€7,200–€14,400
Large60+ pages, custom webpack config, auth, i18n, serverMiddleware, thin test coverage35–60 days2–4 months€16,800–€28,800

The biggest swings come from the UI library, abandoned modules and missing tests. Calendar time assumes your team reviews and merges within a day or two. After the code review you get a fixed price or a day-rate estimate, as on my legacy migrations page.

A step-by-step plan

  1. Audit and plan. The inventory, blockers, replacements, order of work and estimate, in writing.
  2. Build the safety net. Playwright tests for critical flows against the current app, plus error tracking for a baseline.
  3. Prepare on Nuxt 2. Move to Nuxt 2.18.1 and Vue 2.7.16, which backported the Composition API and <script setup>. Replace filters, remove the event bus and start moving Vuex modules to Pinia.
  4. Isolate the UI library. Wrap Vuetify 2 or BootstrapVue components in your own base components, so the swap later happens in one folder.
  5. Open the Nuxt 4 branch. defineNuxtConfig, the app/ directory, Vite, runtimeConfig and Nuxt 4 versions of your modules. Merge main into it daily.
  6. Port pages route by route. asyncData to useAsyncData or useFetch, head() to useSeoMeta, page options to definePageMeta, middleware to defineNuxtRouteMiddleware.
  7. Port the rest. Plugins, serverMiddleware to Nitro server routes, the remaining stores, and the new UI library behind your wrappers.
  8. Verify. Run the end-to-end suite, compare every URL, title, meta description, canonical and sitemap entry with production, and check Core Web Vitals. AI crawlers read the same server-rendered HTML; more on that in how to get recommended by ChatGPT and AI search.
  9. Release in an agreed window. Watch errors and Search Console for the first days, with the old build ready for a rollback.
  10. Clean up and hand over. Remove shims and dead code, update the README and walk your team through what changed.

How to keep shipping features during the migration

A migration that stops the roadmap for two months shouldn't get approved. Here is how features keep moving:

  • Most of the risk ships early. Steps 2 to 4 reach production on Nuxt 2 through your normal releases.
  • New code is written for Vue 3 from day one: <script setup>, Pinia, no filters, no event bus. It ports almost unchanged.
  • The branch follows main daily, so features built meanwhile are ported continuously, not in one painful merge.
  • The freeze is short and planned: a few days around the final merge, agreed in advance.
  • Large apps can move in sections, with Nuxt 4 next to Nuxt 2 behind a reverse proxy, taking over one route group at a time.

If you want this done for you, that is my legacy migrations service: a code review, a written plan with a price, then the upgrade in small tested steps. Afterwards I can keep building through Nuxt development, or Vue.js development for apps behind a login. Send your package.json and a line about the app through the hire me page, and I will reply within one working day.

FAQ

Can I migrate from Nuxt 2 straight to Nuxt 4 and skip Nuxt 3?

Yes, and I recommend it. Nuxt 3 has been end of life since July 31, 2026, so stopping there means planning a second migration immediately. You set up a Nuxt 4 configuration directly and port your code into it.

Is Nuxt Bridge still worth using in 2026?

Rarely. Bridge is still published and brings many Nuxt 3 features to Nuxt 2, but it runs on Nuxt 2, which is end of life. I usually prepare on plain Nuxt 2.18 and Vue 2.7 instead; Bridge is a stepping stone at best, never a destination.

Do I have to rewrite every component in the Composition API?

No. The Options API works in Vue 3, and the Vue team has said it has no plans to deprecate it. I convert components to <script setup> when they are touched for other reasons and leave stable ones alone.

My app is plain Vue 2, not Nuxt. Does this plan still apply?

Mostly. Vuex, filters, the event bus, the UI library and the move from Vue CLI's webpack to Vite are the same work. Vue's migration build, @vue/compat, runs Vue 2-style code on Vue 3 while you fix the warnings one by one. Adding Nuxt pays off only if you need server rendering for SEO.

How much does a Nuxt 2 to Nuxt 4 migration cost?

It depends on size, abandoned dependencies and test coverage. At €480/day, small apps typically land at €3,000–€5,800, medium apps at €7,200–€14,400 and large apps from about €16,800. You get a fixed price or a day-rate estimate after a code review.