github nuxt/scripts v1.0.0

11 hours ago

Nuxt Scripts v1 is the first stable release.

Nuxt Scripts v1 is the first stable release, pushing the ecosystem forward for better privacy and performance for third-party scripts.

📣 Highlights

🔒 First-Party Mode: Privacy Focused Proxy

Every third-party script request exposes your users data to fingerprinting.

Different providers are more intrusive, for example, the X Pixel accesses 9 browser fingerprinting APIs (including navigator.getBattery()), sets 5 tracking cookies, and makes requests to 3 separate domains. Microsoft Clarity reads 10 fingerprinting APIs across 3 domains.

First-party mode acts as a reverse proxy: scripts are bundled at build time and served from your domain, while runtime requests are securely forwarded through your server. Data sent to third-party servers gets anonymised: IPs (180.233.124.74 -> 180.233.124.0), browser versions (Mozilla/5.0 (compatible; Chrome/120.0)) and more. This is auto-enabled for all scripts that support it.

A side-effect is a performance boost from avoiding extra DNS lookups, fewer cookie banners, and reducing expensive fingerprinting queries. It also makes ad-blockers ineffective since requests appear same-origin.

See the First-Party Mode Guide and PR #577 for details.

🛠️ Rebuilt DevTools

The Nuxt DevTools panel has been rewritten around the v1 privacy and capability model. New views surface the things v1 does differently.

Image

🎉 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. Integrates directly with first-party mode. See PR #576.

Set partytown: true per-script:

export default defineNuxtConfig({
  modules: ['@nuxtjs/partytown', '@nuxt/scripts'],
  scripts: {
    registry: {
      plausibleAnalytics: { domain: 'example.com', partytown: true },
      fathomAnalytics: { site: 'XXXXX', partytown: true },
      umamiAnalytics: { websiteId: 'xxx', partytown: true },
    }
  }
  // Forward array auto-configured per-script!
})

Auto-forwarding supported for: googleAnalytics, plausibleAnalytics, fathomAnalytics, umamiAnalytics, matomoAnalytics, segment, mixpanelAnalytics, bingUet, metaPixel, xPixel, tiktokPixel, snapchatPixel, redditPixel, cloudflareWebAnalytics

⚠️ 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, Bluesky posts) hurt performance and leak user data. Following the Cloudflare Zaraz approach, Nuxt Scripts now fetches embed data server-side and securely proxies 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>

<ScriptBlueskyEmbed post-url="https://bsky.app/profile/...">
  <template #default="{ html }">
    <div v-html="html" />
  </template>
</ScriptBlueskyEmbed>

✋ First-Class Consent Controls

v1 treats consent as a first-class concern with two complementary APIs. See the Consent Guide, PR #544, PR #631, and PR #712.

Every consent-aware registry script now exposes a vendor-native consent object on the returned instance plus a typed defaultConsent option applied before the first tracking call.

const gtm = useScriptGoogleTagManager({
  id: 'GTM-XXX',
  defaultConsent: { ad_storage: 'denied', analytics_storage: 'denied' },
})
// Later, after the user accepts
gtm.consent.update({ ad_storage: 'granted', analytics_storage: 'granted' })

const ttq = useScriptTikTokPixel({ id: '...', defaultConsent: 'hold' })
ttq.consent.grant() // or .revoke() / .hold()

const meta = useScriptMetaPixel({ id: '...', defaultConsent: 'denied' })
meta.consent.grant()

📦 New Registry Scripts

  • PostHog Analytics (#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
  • Rybbit Analytics (#453): Privacy-focused open source analytics
  • Databuddy Analytics (#495): Lightweight analytics
  • 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

Other Changes

🔄 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'
    }
  }
})

📊 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,
// cookie analysis, network behavior, tracked data types

🎬 YouTube Player Overhaul

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

📹 Vimeo Player Enhancements

  • Aspect ratio control (#624): New ratio prop, matching YouTube Player API

🗺️ Google Maps Overhaul

The Google Maps integration received a major DX overhaul for v1, making it feel like a native Vue component library rather than a wrapper around options bags.

Declarative SFC Components (#510): 11 composable components for markers, shapes, overlays, clustering, and more. All use Vue's injection system for parent/child communication and clean up automatically on unmount.

Custom Marker Content (#658): The #content slot on ScriptGoogleMapsMarker replaces the default pin with any HTML or Vue template. Build price tags, status badges, or any custom marker visual declaratively.

<ScriptGoogleMapsMarker :position="{ lat: -34.397, lng: 150.644 }">
  <template #content>
    <div class="price-tag">$420k</div>
  </template>
</ScriptGoogleMapsMarker>

Custom Overlay View (#658): ScriptGoogleMapsOverlayView renders arbitrary Vue content at a map position with full styling control. When nested inside a marker, it auto-inherits position and follows the marker during drag. Supports v-model:open for toggling visibility without remounting.

<ScriptGoogleMapsMarker :position="pos" @click="open = !open">
  <ScriptGoogleMapsOverlayView v-model:open="open" anchor="bottom-center" :offset="{ x: 0, y: -50 }">
    <MyCustomPopup @close="open = false" />
  </ScriptGoogleMapsOverlayView>
</ScriptGoogleMapsMarker>

Direct :position Prop: Marker components now accept :position as a top-level prop (no options bag needed for the most common case).

Additional Components:

  • ScriptGoogleMapsStaticMap (#673): The static placeholder is now a standalone component, with images routed through your server so API keys stay server-side. Use it inside #placeholder on ScriptGoogleMaps, or standalone for store locators and contact pages that never need the interactive Maps API.
  • ScriptGoogleMapsGeoJson (#656): Declarative wrapper around google.maps.Data for loading and styling GeoJSON with full event bindings.

Infrastructure:

  • Color mode support (#587): Auto light/dark map switching with mapIds prop
  • Geocode proxy: Server-side geocoding reduces billing and hides API keys
  • Memory leak fixes (#651): useGoogleMapsResource composable ensures all sub-components clean up safely on unmount, even across async boundaries
  • Marker clustering perf (#517, #653): Batch operations with noDraw flag to avoid multiple rerenders

Deprecation: The legacy ScriptGoogleMapsMarker (wrapping google.maps.Marker) and ScriptGoogleMapsAdvancedMarkerElement names have been consolidated into ScriptGoogleMapsMarker (wrapping google.maps.marker.AdvancedMarkerElement). We removed ScriptGoogleMapsPinElement; use the #content slot on ScriptGoogleMapsMarker instead.

🔧 Environment-Variable Config

The module now auto-populates runtimeConfig.public.scripts defaults for any enabled registry entry. Script IDs, keys, and domains resolve from NUXT_PUBLIC_SCRIPTS_* env vars without any runtimeConfig boilerplate. See PR #634.

# .env
NUXT_PUBLIC_SCRIPTS_GOOGLE_ANALYTICS_ID=G-XXXXXX
NUXT_PUBLIC_SCRIPTS_POSTHOG_API_KEY=phc_xxx

⚠️ Breaking Changes & Migration

Upgrading from v0? See the v0 to v1 migration guide for the summary table and before/after diffs for every breaking change and deprecation.

Changelog

   🚨 Breaking Changes

   🚀 Features

   🐞 Bug Fixes

   🏎 Performance

    View changes on GitHub

Don't miss a new scripts release

NewReleases is sending notifications on new releases.