Skip to main content
Migration Guide

Nuxt Scripts 2 moves script ownership and SDK readiness onto the lifecycle APIs introduced in Unhead 3.3.1. This removes component callbacks and trigger listeners as soon as their consumer unmounts, without tearing down a script still used by other components.

Requirements

DependencyRequired version
Node.js>=24
Nuxt>=4.5.1, including Nuxt 5 and Nitro 3
@unhead/vue>=3.3.1 <4
unhead>=3.3.1 <4

Upgrade Nuxt and refresh its locked dependencies before installing v2:

npx nuxi@latest upgrade --force

Run the dedicated migration CLI from the project root. Preview its changes first if the project has dynamic Nuxt configuration:

npx @nuxt/scripts-cli migrate v2 --dry-run
npx @nuxt/scripts-cli migrate v2

The CLI handles static registry configuration and mechanical API renames. It lists dynamic configuration and removed components that need manual follow-up.

The module now stops setup with an actionable error when either Unhead package is missing or outside the supported range.

Registry configuration

Registry entries now accept a flat object, 'mock', or false. Nuxt Scripts 2 removes the deprecated true, 'proxy-only', tuple, nested scriptOptions, and reverseProxyIntercept forms.

 scripts: {
   registry: {
-    googleAnalytics: true,
-    plausibleAnalytics: [{ scriptId: 'YOUR_SCRIPT_ID' }, { proxy: false }],
-    calendly: { scriptOptions: { bundle: false } },
-    posthog: { reverseProxyIntercept: false },
+    googleAnalytics: { trigger: 'onNuxtReady' },
+    plausibleAnalytics: { scriptId: 'YOUR_SCRIPT_ID', proxy: false },
+    calendly: { bundle: false },
+    posthog: { proxy: false },
   },
 }

Nuxt Scripts 2 also removes the deprecated top-level globals array. Use a keyed object so each script has a stable name.

Google Maps

Nuxt Scripts 2 removes these v1 compatibility aliases and components:

RemovedReplacement
center and zoom props on <ScriptGoogleMaps>mapOptions.center and mapOptions.zoom
googleMaps template ref keymapsApi
overlay template ref keyoverlayView
<ScriptGoogleMapsAdvancedMarkerElement><ScriptGoogleMapsMarker>
<ScriptGoogleMapsPinElement>The marker #content slot
<ScriptGoogleMapsHeatmapLayer>A maintained heatmap library such as deck.gl

Google removed HeatmapLayer from Maps JavaScript API v3.65, so Nuxt Scripts no longer ships a component that depends on it.

Nuxt Scripts 2 removes the legacy googleStaticMapsProxy option and the billable Google Maps server proxies. Static maps load directly from Google with the public browser key. resolveQueryToLatLng() uses the client Places service. Apply website and API restrictions to the key, then configure quota limits.

Registry APIs

Nuxt Scripts 2 removes these deprecated registry API shapes:

RemovedReplacement
proxy.rybbit.pageview()proxy.pageview()
proxy.ttq('page')proxy.ttq.page()
proxy.ttq('track', ...)proxy.ttq.track(...)
Matomo trackPageView optionwatch, which defaults to true
Plausible domain and extension optionsscriptId and current init options

Consumer scopes

Every useScript() call now returns an Unhead consumer scope. Component unmount automatically releases callbacks and trigger listeners owned by that call.

  • dispose() releases only the current consumer.
  • signal aborts when you dispose that consumer or any caller removes the shared script.
  • script points to the shared script instance.
  • remove() still removes the shared script for all consumers.

If application code used remove() as component cleanup, switch it to dispose(). In Vue components, manual cleanup is normally no longer necessary.

Custom readiness callbacks

The use option remains supported. Callback-driven SDKs should migrate to resolve({ waitFor }), which automatically removes listeners and rejects pending readiness when the script lifecycle ends.

 const script = useScript('https://example.com/sdk.js', {
-  use: () => readyPromise.then(() => window.example),
+  resolve: ({ waitFor }) => waitFor((resolve) => {
+    window.onExampleReady = () => resolve(window.example)
+    return () => delete window.onExampleReady
+  }),
 })

The bundled Google Maps, YouTube Player, Crisp, and Usercentrics integrations now use this API. load() resolves only after each vendor's concrete SDK API is ready.

Script triggers

Nuxt's idle-timeout, interaction, and service-worker helpers now return Unhead trigger functions. Existing scriptOptions.trigger usage is unchanged. Custom trigger functions may return a cleanup callback; Unhead calls it when the consumer scope is disposed.

Was this page helpful?