Skip to content

v1 Release #594

@harlan-zw

Description

@harlan-zw

Nuxt Scripts v1 is the first stable release.

Nuxt Scripts v1 is all about privacy and performance — route analytics through your own domain, move scripts off the main thread, and render social embeds server-side without loading third-party JavaScript.

📣 Highlights

🔒 First-Party Mode

Third-party scripts expose data that can be used for fingerprinting users accross sites. Every request shares the user's IP address, and scripts can set third-party cookies for cross-site tracking. Ad blockers rightfully block these for privacy reasons.

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

See PR #577.

export default defineNuxtConfig({
  scripts: {
    firstParty: true,
    registry: {
      googleAnalytics: { id: 'G-XXXXXX' },
      metaPixel: { id: '123456' },
    }
  }
})

What happens:

  • Scripts downloaded at build time and served from your domain
  • Collection URLs rewritten to local paths (/_scripts/c/ga)
  • Nitro route rules proxy requests to original endpoints
  • User IPs stay private: third parties see your server's IP, not your users'
  • 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.

⚡ Partytown Web Worker Support

Load third-party scripts off the main thread using Partytown. Scripts run in a web worker, freeing the main thread for your app.

See PR #576.

export default defineNuxtConfig({
  modules: ['@nuxtjs/partytown', '@nuxt/scripts'],
  scripts: {
    partytown: ['plausible', 'fathom', 'umami'],
    registry: {
      plausible: { domain: 'example.com' },
      fathom: { site: 'XXXXX' }
    }
  }
  // Forward array auto-configured — no manual setup needed!
})

Auto-forwarding supported for: googleAnalytics, plausible, fathom, umami, matomo, segment, metaPixel, xPixel, tiktokPixel, snapchatPixel, redditPixel, cloudflareWebAnalytics

⚠️ Note: GA4 has known issues with Partytown. GTM is not compatible (requires DOM access). Consider Plausible, Fathom, or Umami instead.

🐦 SSR Social Embeds

Third-party embed scripts (Twitter widgets, Instagram embeds) hurt performance and leak user data. Following the Cloudflare Zaraz approach, we now fetch embed data server-side and proxy all assets through your domain.

See PR #590.

<ScriptXEmbed tweet-id="1754336034228171055">
  <template #default="{ userName, text, likesFormatted, photos }">
    <!-- Full styling control via scoped slots -->
  </template>
</ScriptXEmbed>

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

Privacy benefits:

  • Zero third-party JavaScript loaded
  • No cookies set by X/Instagram
  • User IPs not shared with third parties
  • All content served from your domain

🔄 Script Reload API

Scripts now expose a .reload() method for re-executing DOM-scanning scripts after SPA navigation.

See commit 77f853b.

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

🔐 Automatic SRI Integrity Hashes

Bundled scripts can automatically generate Subresource Integrity hashes.

See PR #575.

export default defineNuxtConfig({
  scripts: {
    assets: {
      integrity: 'sha384'
    }
  }
})

📦 New Registry Scripts

  • PostHog Analytics (PR #568) — Product analytics with feature flags
  • Google reCAPTCHA v3 (PR #567) — Invisible bot protection
  • TikTok Pixel (PR #569) — Conversion tracking
  • Google Sign-In (PR #573) — One-tap authentication

🎬 YouTube Player Overhaul

  • Isolated player instances (PR #586) — Multiple players work correctly
  • Aspect ratio control — New ratio prop
  • Proper cleanup — Players destroyed on unmount

🗺️ Google Maps Enhancements

  • Color mode support (PR #587) — Auto light/dark switching
  • Static maps proxy — Hide API keys, fix CORS

🏷️ GTM Consent Mode

export default defineNuxtConfig({
  scripts: {
    registry: {
      googleTagManager: {
        id: 'GTM-XXXX',
        defaultConsent: {
          ad_storage: 'denied',
          analytics_storage: 'denied'
        }
      }
    }
  }
})

See PR #544.

⚠️ Breaking Changes

YouTube Player

Aspect Ratio: Now controlled via ratio prop instead of width/height.

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

Placeholder Image: Default object-fit changed from contain to cover. Use placeholder-object-fit="contain" for old behavior.

Google Tag Manager

onBeforeGtmStart Callback: Now fires for cached/pre-initialized scripts. Guard if needed:

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

🐛 Bug Fixes

  • cdfb697 fix(rybbit): queue custom events before script loads (#585)
  • f8ce5a1 fix(gtm): invoke onBeforeGtmStart callback when ID is in config (#584)
  • a8d20b0 fix: add estree-walker as a dependency (#583)
  • 4c79486 fix(plausible): use consistent window reference in clientInit stub (#574)
  • 78367b1 fix(matomo): respect user-provided URL protocol (#572)
  • c685f43 fix: broken type augmenting
  • e2050a2 fix: align templates with existing augments (#589)
  • da3a8cc chore: include .nuxt types (#588)

📖 Migration Guide

Full migration guide: https://scripts.nuxt.com/docs/guides/v1-migration

Breaking Changes by Impact

Medium Impact:

  • YouTube Player: ratio prop replaces width/height aspect calculation
  • YouTube Player: Placeholder object-fit default changed to cover

Low Impact:

  • GTM: onBeforeGtmStart callback timing changed
  • Type templates reorganized (run nuxi prepare)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions