🐞 Bug Fixes
- Target canvas fingerprinting at build-time - by @harlan-zw (b1c23)
We are very excited to move Nuxt hints from alpha stage to 1.0.0 !
Developer experience in Nuxt is truly something we treasure and we hope this module will help you out on finding different ways to make your Nuxt application more performant. We also want to thank every contributors and users of the modules. It is thanks to them that @nuxt/hints is reaching 1.0.0
Our vision at Nuxt has always been that better DX will have an impact on end-user's UX. By releasing this module, the goal is to help Nuxt developers to improve their Nuxt applications.
v1.0.0 comes with several improvement:
4.4.0/4.4.2 is the next minor release (v4.4.2 was published with no changes due to a publishing failure)
createUseFetch and createUseAsyncDataYou can now create custom instances of useFetch and useAsyncData with your own default options (#32300).
// composables/api.ts
// Simple defaults
export const useClientFetch = createUseFetch({
server: false,
})
// Dynamic defaults with full control over merging
export const useApiFetch = createUseFetch((currentOptions) => {
const runtimeConfig = useRuntimeConfig()
return {
...currentOptions,
baseURL: currentOptions.baseURL ?? runtimeConfig.public.baseApiUrl,
}
})
Then use them exactly like useFetch – they're fully typed and support all the same options:
<!-- pages/dashboard.vue -->
<script setup lang="ts">
// Uses your baseURL from runtimeConfig automatically
const { data: users } = await useApiFetch('/users')
</script>
When you pass a plain object, your usage options automatically override the defaults. When you pass a function, you get full control over how options are merged – which means you can compose interceptors, headers, and other complex options however you need.
Under the hood, this is powered by a new Nuxt ad-hoc module that scans your composables directory and automatically registers your custom instances for key injection – so they work seamlessly with SSR, just like useAsyncData and useFetch.
There's also createUseAsyncData for the same pattern with useAsyncData.
We've upgraded to vue-router v5 (#34181), which removes the dependency on unplugin-vue-router. This is the first major vue-router upgrade since Nuxt 3, and it comes with a bunch of improvements under the hood.
For most apps, this should be a transparent upgrade. If you're using unplugin-vue-router directly, you can remove it from your dependencies.
The next step will be taking typed routes out of experimental status. 👀
definePageMetaYou can now pass props to your layouts directly from definePageMeta (#34262). This means your layouts can be parameterised per-page without needing to use provide/inject or other workarounds. Check out the updated docs for the full details.
// pages/dashboard.vue
definePageMeta({
layout: {
name: 'panel',
props: {
sidebar: true,
title: 'Dashboard',
},
},
})
Even better – the props are fully typed (#34409). If your layout defines props, you'll get autocomplete and type-checking in definePageMeta.
<!-- layouts/panel.vue -->
<script setup lang="ts">
defineProps<{
sidebar?: boolean
title?: string
}>()
</script>
useAnnouncer ComposableAccessibility got a major boost with the new useAnnouncer composable and <NuxtAnnouncer> component (#34318). While useRouteAnnouncer handles page navigation for screen readers, many apps need to announce dynamic in-page changes – form submissions, loading states, search results, and more.
<!-- app.vue -->
<template>
<NuxtAnnouncer />
<NuxtRouteAnnouncer />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<!-- components/ContactForm.vue -->
<script setup lang="ts">
const { polite, assertive } = useAnnouncer()
async function submitForm() {
try {
await $fetch('/api/contact', { method: 'POST', body: formData })
polite('Message sent successfully')
}
catch (error) {
assertive('Error: Failed to send message')
}
}
</script>
Note: This is part of our accessibility roadmap. You don't need to use it everywhere – for many interactions, moving focus to new content or using native form validation is sufficient.
useAnnounceris most useful when content changes dynamically without a corresponding focus change.
unroutingWe've migrated Nuxt's file-system route generation to unrouting (#34316), which uses a trie data structure for constructing routes. The cold start is roughly the same (~8ms vs ~6ms for large apps), but dev server changes are up to 28x faster when you're not adding/removing pages, and ~15% faster even when you are.
This also makes route generation more deterministic – it's no longer sensitive to page file ordering.
When a cached route (ISR/SWR) is rendered at runtime with payload extraction enabled, the browser immediately fetches _payload.json as a second request – which triggers a full SSR re-render of the same page. In serverless environments, this can spin up a second lambda before the first response has even finished streaming.
This release addresses this with two changes (#34410):
payloadExtraction: 'client' mode that inlines the full payload in the initial HTML response while still generating _payload.json for client-side navigation_payload.json requests can be served without a full re-render// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
payloadExtraction: 'client',
},
})
Note:
payloadExtraction: 'client'will become the default withcompatibilityVersion: 5. The runtime cache is active for all users.
refresh Option for useCookieIf you're using cookies for session management, you've probably run into the problem of needing to extend a cookie's expiration without changing its value. The new refresh option makes this simple (#33814):
const session = useCookie('session-id', {
maxAge: 60 * 60,
refresh: true,
})
// Extends expiration each time, even with the same value
session.value = session.value
useState Reset to DefaultuseState and clearNuxtState now support resetting to the initial value instead of clearing to undefined (#33527). This aligns with how useAsyncData handles resets and is more intuitive for state management.
const count = useState('counter', () => 0)
count.value = 42
// Resets to 0 (the init value), not undefined
clearNuxtState('counter')
Inspired by features in TanStack Start, import protection now shows suggestions and a full trace of where a problematic import originated (#34454). This makes it much easier to debug why a server-only import ended up in your client bundle.
For example, if you accidentally import from a server route in a component:

The trace shows the import chain (the component was imported from a page), the exact line of code, and actionable suggestions for how to fix it.
We plan to continue work on improving our error messages. 🪵
You can now define view transition types in Nuxt's experimental view transitions support (#31982). This lets you use different transition styles for different navigation patterns (forwards vs. backwards, tabs vs. pages, etc.).
optimizeDeps HintsWhen Vite discovers new dependencies at runtime and triggers page reloads, Nuxt now shows a clear, copy-pasteable nuxt.config.ts snippet to pre-bundle them (#34320). It also warns about unresolvable entries at startup.
A new experimental option normalises page component names to match route names (#33513), which can help with consistency in devtools and debugging.
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
normalizeComponentNames: true,
},
})
Ever wondered where your build time goes? You can now get a detailed performance breakdown of your Nuxt builds (#34468, nuxt/cli#1243):
nuxt build --profile
This produces a report showing duration, RSS delta, and heap delta for every build phase, module, and bundler plugin:

It also profiles individual modules and bundler plugins, making it easy to spot bottlenecks. Three output formats are written:
.nuxt/perf-trace.json) – open in chrome://tracing or Perfetto for a visual timeline.nuxt/perf-report.json) – machine-readable data for tracking over timenuxt-build.cpuprofile) – open in Chrome DevTools or VS Code for flame graphsFor even more detail, use --profile=verbose to print timing breakdowns to the console.
We'll be using this feature to make Nuxt even faster – and if performance is something you care about, this might be a good opportunity to contribute!
new URL() + regex chain with a single indexOf + slice (#34451)Our recommendation for upgrading is to run:
npx nuxt upgrade --dedupe
This will deduplicate your lockfile and help ensure you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem.
Tip: Check out our upgrade guide if upgrading from an older version.
definePageMeta (#34262)useAnnouncer composable and <NuxtAnnouncer> component (#34318)useState and clearNuxtState (#33527)refresh option to useCookie (#33814)#app/composables/pages (#33453)layout property in definePageMeta (#34409)hookable to v6 (#33905)useFetch and useAsyncData (#32300)payloadExtraction: 'client' (#34410)moduleDependencies options (#34330)_payload.json in dev mode (#34365)NuxtLink.useLink (#34380)ssr: false (#34327)rollup-plugin-visualizer to optional peer dep (#34458)asyncNuxtStorage across kit instances (#34492)/h3 subpath export for forward-compat (#34383)destr (#34037)transformWithEsbuild for analysis (#34447)event.context.webpack code (#34502)<NuxtImg> fetch priority example (#34341)useFetch + useLazyFetch docs (#34449)nuxt/.github pr template (5502ccd7b)4x and latest tags (1bc9bfd10)any (cc380ba3b)engines.node following #34458 (#34458)installed-check and engines.node (0c5869c2d)main (6abf31062)issue-needs-reproduction (7849b34c8)issues: write permission to needs-reproduction workflow (ba3412b49)v5 prefix in skip-changelog filter (dbc1a8666)No significant changes
/types-source subpath export for doc gen - by @harlan-zw (e415b)ratio prop - by @AndColla in https://github.com/nuxt/scripts/issues/624 (88ab1)placeholderObjectFit - by @harlan-zw (61b1c)No significant changes
No significant changes
No significant changes
No significant changes
No significant changes
@nuxt/scripts/stats subpath export - by @harlan-zw (202a8)