---
title: "v1 to v2"
description: "Migration guide for upgrading from Nuxt Scripts v1.x to v2.0."
canonical_url: "https://scripts.nuxt.com/docs/migration-guide/v1-to-v2"
last_updated: "2026-08-11T04:27:00.541Z"
---

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

<table>
<thead>
  <tr>
    <th>
      Dependency
    </th>
    
    <th>
      Required version
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <a href="https://nodejs.org" rel="nofollow">
        Node.js
      </a>
    </td>
    
    <td>
      <code>
        >=24
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Nuxt
    </td>
    
    <td>
      <code>
        >=4.5.1
      </code>
      
      , including Nuxt 5 and Nitro 3
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @unhead/vue
      </code>
    </td>
    
    <td>
      <code>
        >=3.3.1 <4
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unhead
      </code>
    </td>
    
    <td>
      <code>
        >=3.3.1 <4
      </code>
    </td>
  </tr>
</tbody>
</table>

Upgrade Nuxt and refresh its locked dependencies before installing v2:

```bash
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:

```bash
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.

```diff
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:

<table>
<thead>
  <tr>
    <th>
      Removed
    </th>
    
    <th>
      Replacement
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        center
      </code>
      
       and <code>
        zoom
      </code>
      
       props on <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          ScriptGoogleMaps
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      <code>
        mapOptions.center
      </code>
      
       and <code>
        mapOptions.zoom
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        googleMaps
      </code>
      
       template ref key
    </td>
    
    <td>
      <code>
        mapsApi
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        overlay
      </code>
      
       template ref key
    </td>
    
    <td>
      <code>
        overlayView
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          ScriptGoogleMapsAdvancedMarkerElement
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          ScriptGoogleMapsMarker
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          ScriptGoogleMapsPinElement
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      The marker <code>
        #content
      </code>
      
       slot
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sFfpx">
          ScriptGoogleMapsHeatmapLayer
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      A maintained heatmap library such as deck.gl
    </td>
  </tr>
</tbody>
</table>

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:

<table>
<thead>
  <tr>
    <th>
      Removed
    </th>
    
    <th>
      Replacement
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code className="language-ts shiki shiki-themes github-light github-light material-theme-palenight" language="ts" style="">
        <span class="sqjlB">
          proxy
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="sqjlB">
          rybbit
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="s0YkB">
          pageview
        </span>
        
        <span class="sqjlB">
          ()
        </span>
      </code>
    </td>
    
    <td>
      <code className="language-ts shiki shiki-themes github-light github-light material-theme-palenight" language="ts" style="">
        <span class="sqjlB">
          proxy
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="s0YkB">
          pageview
        </span>
        
        <span class="sqjlB">
          ()
        </span>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-ts shiki shiki-themes github-light github-light material-theme-palenight" language="ts" style="">
        <span class="sqjlB">
          proxy
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="s0YkB">
          ttq
        </span>
        
        <span class="sqjlB">
          (
        </span>
        
        <span class="sbw7o">
          '
        </span>
        
        <span class="sJnJ8">
          page
        </span>
        
        <span class="sbw7o">
          '
        </span>
        
        <span class="sqjlB">
          )
        </span>
      </code>
    </td>
    
    <td>
      <code className="language-ts shiki shiki-themes github-light github-light material-theme-palenight" language="ts" style="">
        <span class="sqjlB">
          proxy
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="sqjlB">
          ttq
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="s0YkB">
          page
        </span>
        
        <span class="sqjlB">
          ()
        </span>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code className="language-ts shiki shiki-themes github-light github-light material-theme-palenight" language="ts" style="">
        <span class="sqjlB">
          proxy
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="s0YkB">
          ttq
        </span>
        
        <span class="sqjlB">
          (
        </span>
        
        <span class="sbw7o">
          '
        </span>
        
        <span class="sJnJ8">
          track
        </span>
        
        <span class="sbw7o">
          '
        </span>
        
        <span class="sx-uw">
          ,
        </span>
        
        <span class="sc1V3">
          ...
        </span>
        
        <span class="sqjlB">
          )
        </span>
      </code>
    </td>
    
    <td>
      <code className="language-ts shiki shiki-themes github-light github-light material-theme-palenight" language="ts" style="">
        <span class="sqjlB">
          proxy
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="sqjlB">
          ttq
        </span>
        
        <span class="sx-uw">
          .
        </span>
        
        <span class="s0YkB">
          track
        </span>
        
        <span class="sqjlB">
          (
        </span>
        
        <span class="sc1V3">
          ...
        </span>
        
        <span class="sqjlB">
          )
        </span>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Matomo <code>
        trackPageView
      </code>
      
       option
    </td>
    
    <td>
      <code>
        watch
      </code>
      
      , which defaults to <code>
        true
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Plausible <code>
        domain
      </code>
      
       and <code>
        extension
      </code>
      
       options
    </td>
    
    <td>
      <code>
        scriptId
      </code>
      
       and current init options
    </td>
  </tr>
</tbody>
</table>

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

```diff
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.
