Migration Guide

What's New in v1

v1 focuses on privacy and performance: route analytics through your own domain, move scripts off the main thread, and render social embeds server-side.

First-Party Mode (#577)

Third-party scripts expose data that enables fingerprinting users across sites. Every request shares the user's IP address, and scripts can set third-party cookies for cross-site tracking. Different providers are more intrusive - for example, the X Pixel accesses 9 browser fingerprinting APIs (including navigator.getBattery()), sets 5 tracking cookies, and phones home to 3 separate domains. Even Microsoft Clarity reads 10 fingerprinting APIs across 3 domains.

First-party mode routes all script traffic through your own domain.

export default defineNuxtConfig({
  scripts: {
    firstParty: true,
    registry: {
      googleAnalytics: { id: 'G-XXXXXX' },
      metaPixel: { id: '123456' },
    }
  }
})
  • User IPs stay private: third parties see your server's IP
  • No third-party cookies: requests are same-origin
  • Works with ad blockers: requests appear first-party

Supported: Google Analytics, GTM, Meta Pixel, TikTok, Segment, Clarity, Hotjar, X/Twitter, Snapchat, Reddit.

See First-Party Guide for details.

Partytown Support (#576)

Load scripts off the main thread using web workers.

export default defineNuxtConfig({
  modules: ['@nuxtjs/partytown', '@nuxt/scripts'],
  scripts: {
    partytown: ['plausible', 'fathom', 'umami'],
    registry: {
      plausible: { domain: 'example.com' }
    }
  }
})

Forward arrays are auto-configured for supported scripts.

GA4 has known issues with Partytown. GTM is not compatible. Consider Plausible, Fathom, or Umami instead.

SSR Social Embeds (#590)

Render X, Instagram, and Bluesky embeds server-side without loading third-party JavaScript.

Enable the embeds you need in your nuxt.config:

nuxt.config.ts
export default defineNuxtConfig({
  scripts: {
    registry: {
      xEmbed: true,
      instagramEmbed: true,
      blueskyEmbed: true,
    },
  },
})
<ScriptXEmbed tweet-id="1754336034228171055">
  <template #default="{ userName, text, likesFormatted }">
    <!-- Full styling control via scoped slots -->
  </template>
</ScriptXEmbed>

<ScriptInstagramEmbed post-url="https://instagram.com/p/ABC123/">
  <template #default="{ html }">
    <div v-html="html" />
  </template>
</ScriptInstagramEmbed>

<ScriptBlueskyEmbed post-url="https://bsky.app/profile/...">
  <template #default="{ html }">
    <div v-html="html" />
  </template>
</ScriptBlueskyEmbed>
  • Zero third-party JavaScript
  • No cookies set by X/Instagram/Bluesky
  • User IPs not shared
  • All content served from your domain

The useScriptTriggerConsent() composable now supports revoking consent at runtime:

const trigger = useScriptTriggerConsent()
useScriptGoogleTagManager({ id: 'GTM-XXX', scriptOptions: { trigger } })

trigger.accept() // Grant consent
trigger.revoke() // Revoke consent
trigger.consented // Ref<boolean>

Script Reload (77f853b)

Re-execute DOM-scanning scripts after SPA navigation:

const script = useScript('/third-party.js')
await script.reload()

SRI Integrity Hashes (#575)

export default defineNuxtConfig({
  scripts: {
    assets: {
      integrity: 'sha384' // or 'sha256', 'sha512'
    }
  }
})

Script Stats Export

New @nuxt/scripts/stats subpath export for auditing script privacy, performance, and security characteristics.

import { getScriptStats } from '@nuxt/scripts/stats'

const stats = await getScriptStats()
// Privacy ratings (A+ to F), performance ratings, CWV estimates
export default defineNuxtConfig({
  scripts: {
    registry: {
      googleTagManager: {
        id: 'GTM-XXXX',
        defaultConsent: {
          ad_storage: 'denied',
          analytics_storage: 'denied'
        }
      }
    }
  }
})

New Registry Scripts

  • PostHog (#568): Product analytics with feature flags
  • Google reCAPTCHA v3 (#567): Invisible bot protection
  • TikTok Pixel (#569): Conversion tracking
  • Google Sign-In (#573): One-tap authentication
  • Bing UET (#650): Microsoft Advertising conversion tracking
  • Mixpanel Analytics (#648): Product analytics and user tracking
  • Vercel Analytics (#605): Vercel Web Analytics integration
  • Gravatar (#606): Avatar service with privacy-preserving proxy

Vimeo Player Enhancements (#624)

New ratio prop for aspect ratio control, matching YouTube Player API.

Google Maps Color Mode (#587)

<ScriptGoogleMaps
  :map-ids="{ light: 'LIGHT_MAP_ID', dark: 'DARK_MAP_ID' }"
/>

Auto-switches with @nuxtjs/color-mode or manual color-mode prop.

Google Maps Geocode Proxy

Server-side geocoding proxy to reduce billing costs and hide API keys. Automatically enabled when googleMaps is in your registry.

Breaking Changes

PayPal SDK v6 (#628)

Migrated to PayPal JavaScript SDK v6. The component API has changed significantly:

  • SDK URLs now use v6 endpoints (/web-sdk/v6/core)
  • New authentication modes: clientId (with optional clientToken) or clientToken alone
  • Sandbox mode defaults to true in development

YouTube Player

Aspect Ratio

Use ratio prop instead of deriving from width/height:

<ScriptYouTubePlayer
  video-id="..."
-  :width="1280"
-  :height="720"
+  ratio="16/9"
/>

Default is 16/9.

Placeholder Image

Default object-fit changed from contain to cover:

<ScriptYouTubePlayer video-id="..." placeholder-object-fit="contain" />

Multiple Players

Player instances are now properly isolated. Remove any workarounds for multiple players.

Google Tag Manager

onBeforeGtmStart Callback

Now fires for cached/pre-initialized scripts. Guard against multiple calls:

let initialized = false
useScriptGoogleTagManager({
  onBeforeGtmStart: (gtag) => {
    if (initialized)
      return
    initialized = true
    // your init code
  }
})

Type Augmentation

Templates reorganized. Run nuxi prepare after upgrading.