Nuxt 3 reached end of life on 31 July 2026. Your app still runs, but it no longer gets security patches or bug fixes, and new module releases are moving to Nuxt 4.
The good news is that the Nuxt 3 → 4 upgrade is much smaller than Nuxt 2 → 3 was. The bad news is that most of the breaking changes don't throw errors. They change behaviour quietly, so things look fine until a user notices.
After going through the official upgrade guide line by line, these are the seven changes I'd check first.
- data and error are now undefined, not null
const { data } = await useFetch('/api/product') if (data.value === null) showEmptyState() // never true in Nuxt 4 Anything that checks for null quietly stops working. Use data.value == null, a falsy check, or compare with undefined.
- data is a shallowRef
data.value.title = 'New title' // no longer re-renders
Changing a nested property no longer triggers reactivity. Either replace data.value entirely, or pass { deep: true } to that specific useFetch/useAsyncData call.
- pending means something different
pending is now only true while status === 'pending'. With immediate: false it stays false until the first request, so loading spinners that relied on it can disappear. Prefer checking status.
- dedupe no longer takes booleans
refresh({ dedupe: true }) // before refresh({ dedupe: 'cancel' }) // now ('defer' replaces false) - Keys are shared, and so is prerendered data
Calls with the same key now share data, error and status. If two components use the same key with different transform or default options, you'll get warnings and surprising results. Prerendered pages also share data by key, so include route params in your keys.
- Removed APIs and config
window.NUXT is gone after hydration. Use useNuxtApp().payload.
route.meta.name → route.name for values set in definePageMeta.
Top-level generate config is gone. Move it to nitro.prerender.routes / nitro.prerender.ignore.
error.data is parsed for you, so remove JSON.parse in error.vue.
- Unhead v2 and component names
hid and vmid head props are removed (use key). Component names are normalised to their Nuxt-style name (SomeFolderMyComponent), which can break lists and test helpers like findComponent.
Finding all of this in your codebase
I got tired of grepping for these by hand, so I wrote a small CLI that does it:
npx nuxt4-check
It's zero-dependency and read-only, and nothing leaves your machine. It prints each issue with file:line, lists the modules you should verify for Nuxt 4 support, and gives a rough effort estimate. Add --md report.md to get a report you can paste into a ticket, or --json for CI. It exits with code 1 when it finds high-severity issues, so it can act as a gate.
Then run the official codemods, which fix several of these automatically:
npx codemod@0.18.7 nuxt/4/migration-recipe
It's a heuristic scan, so treat it as a checklist rather than a guarantee. If you hit a false positive or a miss, open an issue: https://github.com/atlantikjashari/nuxt4-check
I'm Atlantik, a Vue/Nuxt developer. If you'd rather hand the upgrade off, I do fixed-price Nuxt 4 upgrades delivered as one reviewable PR: atlantik.jashari@gmail.com.



