Skip to content
Release notesNuxt

Nuxt 3 reaches end of life on 31 July: a checklist

Nuxt 4.5.1 and 3.21.10 shipped on 27 July with a batch of security fixes. Four days later the 3.x line reaches end of life. Here is what the patches fix and the order to work in.

vuejs.school team28 Jul 20267 min readNuxt 4.5.1 · Nuxt 3.21.10

Nuxt 3 reaches end of life on Friday, 31 July. On Monday the Nuxt team published security patch releases for both lines, Nuxt 4.5.1 and Nuxt 3.21.10, so the last week of Nuxt 3 starts with an upgrade you should make today, whichever line you are on.

If your app is still on Nuxt 3, the five steps below take it from today's patch to Nuxt 4.5.1, in an order that keeps production patched while you work.

What end of life means for a 3.x app

End of life means the 3.x line is no longer maintained. Your app keeps running on 1 August. What stops is the flow of fixes: a vulnerability reported after Friday gets a patch for Nuxt 4, and no 3.x release is promised for it.

The date has moved once. When Nuxt 4.0 shipped on 15 July 2025, its announcement promised Nuxt 3 maintenance (bug fixes and backports from 4) until the end of January 2026. The Nuxt 4.3 post of 22 January extended that: security updates and critical bug fixes would continue, and Nuxt 3 would meet its end of life on 31 July 2026. The Nuxt 4.5 release on 18 July repeated the date and called its companion, 3.21.9, one of the last few 3.x releases.

What the 27 July patches fix

The security post groups its advisories under seven headings, six for Nuxt and one for Nuxt DevTools. Each entry below says who is affected:

  • Remote code execution through server island props (High). Only if vue.runtimeCompiler is switched on (it is off by default) and island props can reach dynamic component resolution such as <component :is>.
  • Unauthorised component instantiation through island props (Medium). No runtime compiler needed. An island that forwards an undeclared prop onto a polymorphic as root could be made to render an arbitrary element or component. It does not allow code execution.
  • Route rule authorisation bypass (High). Apps that use routeRules with appMiddleware as an access check, where a rule key contains an uppercase letter, for example one derived from pages/Admin.vue. This is a regression introduced by the fix for CVE-2026-53721, so being on 4.4.7 or 3.21.7 does not protect you.
  • Server component denial of service (High). Any app with server components or islands. An unauthenticated request could crash the server.
  • Cross-user payload disclosure on cached pages (High). 4.x only, from 4.4.0. With cache, swr or isr route rules on pages that show per-user data, the cached _payload.json could reach another visitor.
  • Dev server path disclosure (Low). Only when running nuxi dev --host on an untrusted network.
  • DevTools remote code execution (Critical, development only). Anyone who runs nuxt dev with DevTools enabled. Fixed in @nuxt/devtools 3.3.1, which a refreshed lockfile pulls in.

Two follow-ups after upgrading:

  • Route rules now match case-insensitively, like Vue Router. If you relied on case-sensitive matching, set router.options.sensitive: true in your config.
  • On 4.x, purge your CDN if you cache authenticated pages. A leaked _payload.json may already sit in an edge cache, and upgrading does not evict it.

If you are pinned to an older release and cannot upgrade yet, the post points to Socket's Certified Patches as a stopgap, while saying that upgrading remains the recommended fix.

Step 1: patch to 3.21.10 today

On a 3.x project, keep the upgrade on the 3.x line with the --channel=v3 flag (the command the Nuxt 4.5 post gives), then confirm what you got:

npx nuxt@latest upgrade --dedupe --channel=v3
npm ls nuxt @nuxt/devtools

--dedupe rewrites the lockfile, which is how @nuxt/devtools 3.3.1 arrives. The second command should print nuxt@3.21.10 and a DevTools version of 3.3.1 or later. Run your tests, deploy, and commit this change on its own.

Step 2: audit your modules

Modules are where an upgrade stalls. Open nuxt.config.ts, copy the modules array into a list, and check each one's latest release for Nuxt 4 support. Sort them into three groups: already works on 4, has a newer major that does, and unmaintained.

Nuxt helps with this. A module can declare which Nuxt versions it supports in its meta.compatibility. When the running Nuxt falls outside that range, Nuxt skips the module and logs Module <name> is disabled due to incompatibility issues. A skipped module is easy to miss in a busy dev log, and the app may still boot without it. During the migration, make that warning an error:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/image', '@nuxtjs/i18n'],
  experimental: {
    enforceModuleCompatibility: true,
  },
})

With the flag on, an incompatible module stops the build with a ModuleCompatibilityError instead of disappearing quietly. Upgrade the modules that have a release supporting both 3 and 4 now, while you are still on 3, so each change is tested on its own. For an unmaintained module, look for a replacement before you touch Nuxt.

Step 3: move to Node 24 before Nuxt 4

Nuxt 4.5.1 accepts fewer Node versions than Nuxt 3.21.10:

Run node --version on your machine, in CI and on your hosting platform. Node 20 reached its own end of life on 30 April, so a project still on it has two unsupported pieces, not one. Move to Node 24.11 or a later 24.x release (Node 24 is the active long-term support line), and do it as a separate change on Nuxt 3.21.10, which runs on both Node 20 and Node 24.

Step 4: turn on Nuxt 4 behaviour while still on 3

Since Nuxt 3.12, a 3.x project can opt in to many of Nuxt 4's breaking changes with one setting. This is the bridge: you fix what breaks with the old version still installed, and the final upgrade becomes small.

nuxt.config.ts
export default defineNuxtConfig({
  future: {
    compatibilityVersion: 4,
  },
})

If your project uses layers, the 3.x upgrade guide says each layer that opts in needs this setting too. Then run the codemods the upgrade guide recommends. The guide pins the version because of a bug in later releases:

npx codemod@0.18.7 nuxt/4/migration-recipe

The recipe runs its codemods one after another and lets you deselect any of them. It moves pages/, components/, composables/ and the other app folders into app/ (with server/ staying at the root) and rewrites code for several of the data-fetching changes.

One of those changes needs a decision. With Nuxt 4 behaviour on, data from useFetch and useAsyncData is a shallow ref: fetching new data still updates the page, but changing a property inside it no longer re-renders anything. The recipe's shallow-function-reactivity step adds { deep: true } to the calls it finds, which brings back Nuxt 3's deep reactivity and gives up the performance gain for deeply nested data.

That step is worth deselecting. The guide says most code needs no change, so fix by hand only the code that changes data in place, like this page:

app/pages/todos.vueBreaks
<script setup lang="ts">
interface Todo { id: number, title: string, done: boolean }

const { data: todos } = await useFetch<Todo[]>('/api/todos')

function markDone(todo: Todo) {
  todo.done = true
}
</script>

<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <button type="button" @click="markDone(todo)">
        {{ todo.done ? 'Done' : todo.title }}
      </button>
    </li>
  </ul>
</template>

The click changes the object, but nothing updates on screen. Opt that one call back in to deep reactivity:

app/pages/todos.vueWorks
const { data: todos } = await useFetch<Todo[]>('/api/todos', { deep: true })

Also search for === null checks on data and error: both now start as undefined.

Step 5: upgrade to Nuxt 4.5.1

When the app is green with compatibilityVersion: 4, install Nuxt 4, refresh the lockfile, and remove the future block:

npm install nuxt@^4.5.1
npx nuxt upgrade --dedupe

The second command is the one the Nuxt 4.5 post recommends: it deduplicates the lockfile so the libraries Nuxt depends on move with it. Three major versions change underneath you at once:

  • Vite 7 to 8. The Nuxt 4.5 post asks you to check custom Vite plugins and config against the Vite migration guide.
  • Vue Router 4.6 to 5, which Nuxt switched to in 4.4.
  • unhead 2 to 3, the library behind useHead. The Nuxt 4.5 post warns that its stricter useHead types can surface type errors.

Run nuxt typecheck and your test suite, and read what fails before reaching for workarounds.

Our step-by-step walkthrough of upgrading a small Nuxt 3 app, written a week after Nuxt 4.0 shipped, shows each of these stages with the diff to expect. It still holds; add the Node and Vite notes from this post.

A timeline for teams starting this week

Friday is not a migration deadline. It is the day the safety net goes. A sensible order:

  1. Today: patch to 3.21.10 and deploy.
  2. This week: the module list, the Node upgrade, and enforceModuleCompatibility.
  3. Next week: a branch with compatibilityVersion: 4 and the codemods, merged when tests pass.
  4. The week after: the upgrade to 4.5.1, deployed to a preview environment first.

A team of one with a small app can get through items 2 to 4 in a couple of days. A large app with many modules should still finish within the month, because every week on 3.x after Friday is a week without security fixes.

Check it stuck3 questions from the Modules, plugins and layers quiz.
Practice now
Was this helpful?

Keep reading