Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 2 min read

The 7 Nuxt 4 changes that actually break apps (and how to find them in 10 seconds)

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 sm

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.

  1. 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.

  1. 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.

  1. 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.

  1. dedupe no longer takes booleans
refresh({ dedupe: true })   // before
refresh({ dedupe: 'cancel' }) // now ('defer' replaces false)
  1. 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.

  1. 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.

  1. 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 [email protected] 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: [email protected].

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.