useAsyncData/useFetch return shallow data refs by default. Nested mutations do not trigger reactive updates as they did with deep refs.
const { data } = await useFetch('/api/profile')
data.value.preferences.theme = 'dark'
const { data } = await useFetch('/api/profile', { deep: true })
if (data.value) data.value.preferences.theme = 'dark'
Migration Prefer immutable replacement; set deep: true per call when nested mutations are intentional and test dependent views.
Calls with the same useAsyncData key share data/error/status. Handler, deep, transform, pick, getCachedData and default must be consistent.
useAsyncData('users', () => $fetch('/api/users'), { deep: true })
useAsyncData('users', () => $fetch('/api/users'), { deep: false })
export const useUsers = () => useAsyncData(
'users', () => $fetch('/api/users'), { deep: false }
)
// All consumers call useUsers().
Migration Consolidate a key's options in one composable. Use distinct keys for genuinely different resources/options.
Custom cached-data handlers are invoked for refresh/watch as well as initial fetch. Ignoring the cause can prevent an intended network refresh.
getCachedData: key => cache[key]
getCachedData: (key, nuxtApp, ctx) => {
if (ctx.cause === 'refresh:manual' || ctx.cause === 'refresh:hook') return undefined
return cache[key]
}
Migration Use the context cause to bypass cached data on explicit refresh and review watch-triggered caching.
The initial empty value changes from null to undefined. Strict null-only checks can fail to recognize unloaded data.
if (data.value === null) {
showPlaceholder()
}
if (data.value == null) {
showPlaceholder()
}
Migration Review initial defaults and checks; use nullish checks when both null and undefined represent an empty state.
pending is derived from status === pending. With immediate: false it starts false rather than treating idle as pending.
<script setup>
const { pending } = await useFetch('/api/items', { immediate: false })
</script>
<template><p v-if="pending">Not loaded yet</p></template>
<script setup>
const { status, execute } = await useFetch('/api/items', { immediate: false })
</script>
<template>
<button v-if="status === 'idle'" @click="execute()">Load</button>
<p v-if="status === 'pending'">Loading</p>
</template>
Migration Use status to distinguish idle, pending, success and error; explicitly execute deferred requests.
Clearing async data now restores its default factory value instead of always setting data to undefined.
const { data, clear } = await useFetch('/api/items', { default: () => [] })
clear()
// Previously data reset to undefined.
const { data, clear } = await useFetch('/api/items', { default: () => [] })
clear()
// data is [] again; test length for an empty list.
Migration Update reset logic and placeholder checks if relying on clear to remove a default value.
Payload data may be reused across prerendered pages. A static key for route-dependent content can produce the wrong page data.
const route = useRoute()
const { data } = await useAsyncData('article', () =>
$fetch('/api/articles/' + route.params.slug)
)
const route = useRoute()
const key = computed(() => 'article:' + route.params.slug)
const { data } = await useAsyncData(key, () =>
$fetch('/api/articles/' + route.params.slug)
)
Migration Make keys identify the resource, including relevant route parameters, and verify prerendered HTML and payloads.
Nuxt error data is parsed rather than exposed as a JSON string. An extra JSON.parse can fail on error pages.
const details = JSON.parse(error.data)
const details = error.data
// Validate details before reading properties.
Migration Consume error.data directly and validate its shape before rendering.
Nuxt scans page metadata after pages:extend. Metadata declared in pages may overwrite earlier hook changes.
export default defineNuxtConfig({
hooks: { 'pages:extend': pages => {
for (const page of pages) page.meta = { ...page.meta, layout: 'admin' }
} }
})
export default defineNuxtConfig({
hooks: { 'pages:resolved': pages => {
for (const page of pages) page.meta = { ...page.meta, layout: 'admin' }
} }
})
Migration Use pages:resolved for overrides that must win over definePageMeta and test final routes.