-
diff --git a/docs/composables/useScriptsRegistry.ts b/docs/composables/useScriptsRegistry.ts
deleted file mode 100644
index 116152a2..00000000
--- a/docs/composables/useScriptsRegistry.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { registry } from '../../src/registry'
-
-export function useScriptsRegistry() {
- return registry() // we don't need paths here
-}
diff --git a/docs/content/_cookie-api.md b/docs/content/_cookie-api.md
deleted file mode 100644
index 7db1d329..00000000
--- a/docs/content/_cookie-api.md
+++ /dev/null
@@ -1,11 +0,0 @@
-```vue
-
-
-
-
-```
diff --git a/docs/content/_magic-api.md b/docs/content/_magic-api.md
deleted file mode 100644
index 0590ae9e..00000000
--- a/docs/content/_magic-api.md
+++ /dev/null
@@ -1,14 +0,0 @@
-```vue
-
-```
diff --git a/docs/content/docs/1.getting-started/_dir.yml b/docs/content/docs/1.getting-started/.navigation.yml
similarity index 100%
rename from docs/content/docs/1.getting-started/_dir.yml
rename to docs/content/docs/1.getting-started/.navigation.yml
diff --git a/docs/content/docs/1.getting-started/1.index.md b/docs/content/docs/1.getting-started/1.index.md
index c4952080..ca1a04f0 100644
--- a/docs/content/docs/1.getting-started/1.index.md
+++ b/docs/content/docs/1.getting-started/1.index.md
@@ -1,6 +1,8 @@
---
+
title: Introduction
description: Nuxt Scripts is Nuxt DX for third-party scripts.
+
---
Nuxt Scripts enhances the performance, privacy, and developer experience (DX) when incorporating third-party scripts into Nuxt applications.
@@ -22,13 +24,13 @@ Using the `useHead` composable to load third-party IIFE scripts is straightforwa
Third-party resources like analytics tools, video embeds, maps, and social media integrations enhance website functionality but aren't directly managed by site owners. A single resource may have a minimal performance impact, but multiple resources can significantly degrade user experience. Scripts, in particular, can delay interactivity and obstruct page rendering.
-According to the Chrome User Experience Report, Nuxt sites with numerous third-party resources typically show lower [Interaction to Next Paint (INP)](https://web.dev/articles/inp) and [Largest Contentful Paint (LCP)](https://web.dev/articles/lcp) scores. Despite the correlation not proving causation, lab tests and the [Web Almanac](https://almanac.httparchive.org/en/2022/third-parties) confirm significant performance impacts from third-party resources.
+According to the Chrome User Experience Report, Nuxt sites with numerous third-party resources typically show lower [Interaction to Next Paint (INP)](https://web.dev/articles/inp) and [Largest Contentful Paint (LCP)](https://web.dev/articles/lcp) scores. Despite the correlation not proving causation, lab tests and the [2022 Web Almanac chapter on Third Parties](https://almanac.httparchive.org/en/2022/third-parties) confirm significant performance impacts from third-party resources.
## Nuxt Script Features
### 🏎️ Performance
-- Script loading is triggered only when Nuxt is ready, by default.
+- Nuxt triggers script loading only when ready, by default.
- More advanced triggering of script loads, independent of implementation specifics.
- Improved script loading times with [Bundling Remote Scripts](/docs/guides/bundling).
diff --git a/docs/content/docs/1.getting-started/2.installation.md b/docs/content/docs/1.getting-started/2.installation.md
index 564d8916..a4088f5f 100644
--- a/docs/content/docs/1.getting-started/2.installation.md
+++ b/docs/content/docs/1.getting-started/2.installation.md
@@ -1,6 +1,8 @@
---
+
title: Installation
description: Learn how to create a Nuxt Scripts project or add it to your current Nuxt project.
+
---
## Quick Start
@@ -8,10 +10,16 @@ description: Learn how to create a Nuxt Scripts project or add it to your curren
To get started, simply run:
```bash
-npx nuxi@latest module add @nuxt/scripts
+npx nuxi@latest module add scripts@beta
```
-That's it! The Nuxt Scripts module should be downloaded and added to your Nuxt Config `modules`.
+> [!TIP]
+> Generate an Agent Skill for this package using [skilld](https://github.com/harlan-zw/skilld):
+> ```bash
+> npx skilld add @nuxt/scripts
+> ```
+
+That's it. The Nuxt Scripts module should be downloaded and added to your Nuxt Config `modules`.
## Next Steps
@@ -20,5 +28,5 @@ Need some inspiration to start using Nuxt Scripts? Try out the following:
1. 🎉 Make it rain emojis with the [Confetti Tutorial](/docs/getting-started/confetti-tutorial).
2. 📚 Learn about how the [Script Loading](/docs/guides/script-triggers) works.
3. 🔍 Explore the [Script Registry](/scripts) for popular pre-configured third-party scripts.
-3. 🚀 Load other scripts with [useScript](/docs/api/use-script) or [Global Scripts](/docs/guides/global).
+3. 🚀 Load other scripts with [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} or [Global Scripts](/docs/guides/global).
4. 🔨 Fine-tune your performance and privacy with [Bundling](/docs/guides/bundling) and [Consent Management](/docs/guides/consent).
diff --git a/docs/content/docs/1.getting-started/3.confetti-tutorial.md b/docs/content/docs/1.getting-started/3.confetti-tutorial.md
index c8936a0f..27a32f63 100644
--- a/docs/content/docs/1.getting-started/3.confetti-tutorial.md
+++ b/docs/content/docs/1.getting-started/3.confetti-tutorial.md
@@ -1,6 +1,8 @@
---
+
title: "Tutorial: Load js-confetti"
description: "Learn how to load the js-confetti script using the Nuxt Scripts module."
+
---
## Introduction
@@ -8,24 +10,24 @@ description: "Learn how to load the js-confetti script using the Nuxt Scripts mo
In this tutorial, you will learn how to load the [js-confetti](https://github.com/loonywizard/js-confetti) script using the Nuxt Scripts module.
You'll learn about the following:
-- What the `useScriptNpm` registry script is.
+- What the [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"} registry script is.
- How to load the `js-confetti` script using it.
- Adding types to loaded scripts.
- Using [proxied functions](/docs/guides/key-concepts#understanding-proxied-functions) to call the script.
-## Background on useScriptNpm
+## Background on [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"}
-To load the script, we'll be using the [useScriptNpm](/scripts/utility/npm).
+To load the script, we'll be using the [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"}.
This is a [registry script](/scripts), a supported
third-party integration built on top of the
-[useScript](/docs/api/use-script) composable that allows you to load scripts from NPM.
+[`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} composable that allows you to load scripts from [npm](https://npmjs.com).
-When working with NPM files, you'd typically include them as a node_module dependency in the `package.json` file. However,
+When working with npm files, you'd typically include them as a `node_module` dependency in the `package.json` file. However,
optimizing the script loading of these scripts can be difficult, requiring a dynamic import of the module from a separate chunk and
loading it only when needed. It also slows down your build as the module needs to be transpiled.
-The `useScriptNpm` registry script abstracts this process, allowing you to load scripts that have been exported as immediately invokable functions,
+The [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"} registry script abstracts this process, allowing you to load scripts that export immediately invokable functions,
with a single line of code .
In many instances it will still make more sense to include the script as a dependency in the `package.json` file, but for scripts that are not used often or
@@ -50,7 +52,7 @@ useScript('https://cdn.jsdelivr.net/npm/js-confetti@0.12.0/dist/js-confetti.brow
```ts [useHead]
useHead({
- scripts: [
+ script: [
{ src: 'https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js' }
]
})
@@ -60,7 +62,7 @@ useHead({
### Loading the script
-Within your one of your components, you'll want to load the script. You can do this by using the `useScriptNpm` registry script.
+Within one of your components, you'll want to load the script. You can do this by using the [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"} registry script.
```vue [app.vue]
```
-If you check your browser requests, you should see the script being loaded.
+If you check your browser requests, you should see the script load.
### Resolving the third-party script API
-Now that the script is loaded, you can use it in your component. To do so we need to tell the underlying API how to use the script, for this we can
-leverage the [use](/docs/api/use-script#use) function.
+Now that the script loads, you can use it in your component. To do so we need to tell the basic API how to use the script, for this we can
+Use the [use](/docs/api/use-script#use) function.
-This function is only called on the client-side and is used to resolve the third-party script.
+This function runs only on the client-side to resolve the third-party script.
```vue [app.vue]
```
-### Bonus: Event based script loading
+### Bonus: Trigger-based script loading
You can delay the loading of the script by using the `trigger` option. This can be useful if you want to load the script after a certain event or time.
-In this example we'll combine the [useScriptTriggerElement](/docs/api/use-script-trigger-element) composable with the `useScriptNpm` composable to load the script after a certain element is in view.
+See the [Script Triggers](/docs/guides/script-triggers) guide for all available options.
+
+#### Using a Ref
+
+The simplest approach is to use a `ref` - the script loads when the ref becomes truthy.
+
+```vue [app.vue]
+
+
+
+
+
+```
+
+::tip
+You can also use a computed ref or getter function: `trigger: computed(() => someCondition.value)`{lang="ts"} or `trigger: () => shouldLoad.value`.
+::
+
+#### Using Element Events
+
+You can also use the [`useScriptTriggerElement()`{lang="ts"}](/docs/api/use-script-trigger-element){lang="ts"} composable to trigger loading based on element interactions.
```vue [app.vue]
-
@@ -214,3 +223,103 @@ export function useFathomAnalytics() {
})
}
```
+
+## Extending the Script Registry
+
+You can extend the script registry using the `scripts:registry` hook in your `nuxt.config.ts`:
+
+```ts [nuxt.config.ts]
+import { createResolver } from '@nuxt/kit'
+
+const { resolve } = createResolver(import.meta.url)
+
+export default defineNuxtConfig({
+ modules: ['@nuxt/scripts'],
+
+ hooks: {
+ 'scripts:registry': function (registry) {
+ registry.push({
+ category: 'custom',
+ label: 'My Custom Analytics',
+ logo: '', // optional
+ import: {
+ name: 'useScriptMyAnalytics',
+ from: resolve('./composables/useScriptMyAnalytics'),
+ },
+ })
+ },
+ },
+
+ devtools: {
+ enabled: true,
+ },
+})
+```
+
+Then create your custom script composable:
+
+```ts [composables/useScriptMyAnalytics.ts]
+import { object, string } from '#nuxt-scripts-validator'
+import { useRegistryScript } from '#nuxt-scripts/utils'
+
+export interface MyAnalyticsApi {
+ track: (event: string, data?: Record) => void
+ identify: (userId: string) => void
+}
+
+// Schema for validation and DevTools metadata
+const MyAnalyticsSchema = object({
+ apiKey: string(),
+})
+
+export function useScriptMyAnalytics(options?: {
+ apiKey: string
+ scriptOptions?: NuxtUseScriptOptions
+}) {
+ return useRegistryScript('myAnalytics', () => ({
+ scriptInput: {
+ src: 'https://analytics.example.com/sdk.js',
+ },
+ scriptOptions: {
+ ...options?.scriptOptions,
+ use() {
+ // Initialize your script
+ window.MyAnalytics.init(options?.apiKey)
+ return window.MyAnalytics as T
+ },
+ },
+ }), options, { schema: MyAnalyticsSchema })
+}
+```
+
+### Using Custom Registry Scripts
+
+Once registered, your custom scripts work exactly like built-in registry scripts:
+
+```vue [pages/index.vue]
+
+
+
+
+ Track This Click
+
+
Status: {{ status }}
+
+```
+
+### DevTools Integration
+
+When you include a validation schema, Nuxt Scripts automatically populates DevTools metadata with your script's configuration. The `registryKey` and `registryMeta` are automatically set in development mode, allowing you to:
diff --git a/docs/content/docs/1.guides/1.script-triggers.md b/docs/content/docs/1.guides/1.script-triggers.md
index 9655c4a7..fc18a0ad 100644
--- a/docs/content/docs/1.guides/1.script-triggers.md
+++ b/docs/content/docs/1.guides/1.script-triggers.md
@@ -1,37 +1,121 @@
---
-title: Triggering Script Loading
+
+title: Script Triggers
+description: Control when scripts load with Nuxt Scripts' flexible trigger system.
+
---
-Nuxt Scripts provides several ways to trigger the loading of scripts.
+::callout{icon="i-heroicons-play" to="https://stackblitz.com/github/nuxt/scripts/tree/main/examples/performance" target="_blank"}
+Try the live [Performance Example](https://stackblitz.com/github/nuxt/scripts/tree/main/examples/performance) on [StackBlitz](https://stackblitz.com) to see triggers in action.
+::
+
+Nuxt Scripts provides a flexible trigger system to control when scripts load, helping you optimize performance by loading scripts at the right moment for your users.
+
+## How Triggers Work
+
+Pass any reactive value as `trigger` - the script loads when it becomes truthy:
+
+```ts
+const shouldLoad = ref(false)
+
+useScript('https://example.com/script.js', {
+ trigger: shouldLoad
+})
+
+// Later: trigger loading
+shouldLoad.value = true
+```
+
+This works with refs, computed refs, getter functions, and promises:
+
+```ts
+// Ref
+trigger: shouldLoad
+
+// Computed
+trigger: computed(() => !!route.query.affiliateId)
+
+// Getter function
+trigger: () => shouldLoad.value
+
+// Promise
+trigger: new Promise(resolve => setTimeout(resolve, 3000))
+```
+
+## Default: onNuxtReady
+
+By default, scripts use the `onNuxtReady` trigger, providing "idle loading" behavior where scripts load only after the page is fully interactive. This minimizes impact on Core Web Vitals and user experience.
+
+The `onNuxtReady` trigger ensures scripts load:
+- After Nuxt hydration is complete
+- When the browser is idle and the main thread is available
+- Without blocking critical page rendering or user interactions
+
+```ts
+// Default behavior - explicit for clarity
+useScript('https://widget.intercom.io/widget/abc123', {
+ trigger: 'onNuxtReady'
+})
+
+// Registry scripts also use onNuxtReady by default
+useScriptGoogleAnalytics({
+ id: 'GA_MEASUREMENT_ID'
+ // trigger: 'onNuxtReady' is implied
+})
+```
+
+You can change this default by modifying the [defaultScriptOptions](/docs/api/nuxt-config#defaultscriptoptions).
+
+## Specialized Triggers
+
+### Idle Timeout
+
+Use [`useScriptTriggerIdleTimeout()`{lang="ts"}](/docs/api/use-script-trigger-idle-timeout){lang="ts"} to delay script loading for a specified time after Nuxt is ready:
::code-group
-```ts [useScript]
-useScript({
- src: 'https://example.com/script.js',
-}, {
- // load however you like!
- trigger: new Promise(resolve => setTimeout(resolve, 3000))
+```ts [Composable]
+useScript('https://example.com/analytics.js', {
+ trigger: useScriptTriggerIdleTimeout({ timeout: 5000 })
})
```
-```ts [Registry Script]
-useScriptMetaPixel({
- id: '1234567890',
- scriptOptions: {
- // load however you like!
- trigger: new Promise(resolve => setTimeout(resolve, 3000))
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleAnalytics: [{
+ id: 'GA_MEASUREMENT_ID'
+ }, {
+ trigger: { idleTimeout: 3000 }
+ }]
+ }
}
})
```
-```ts [Global Script]
+::
+
+### User Interaction
+
+Use [`useScriptTriggerInteraction()`{lang="ts"}](/docs/api/use-script-trigger-interaction){lang="ts"} to load scripts when users interact with your site:
+
+::code-group
+
+```ts [Composable]
+useScript('https://example.com/chat-widget.js', {
+ trigger: useScriptTriggerInteraction({
+ events: ['scroll', 'click', 'keydown']
+ })
+})
+```
+
+```ts [nuxt.config.ts]
export default defineNuxtConfig({
scripts: {
globals: {
- myScript: ['https://example.com/script.js', {
- // load however you like!
- trigger: 'onNuxtReady'
+ chatWidget: ['https://widget.example.com/chat.js', {
+ trigger: { interaction: ['scroll', 'click', 'touchstart'] }
}]
}
}
@@ -40,50 +124,32 @@ export default defineNuxtConfig({
::
-## Default Behavior
-
-By default, scripts are loaded when Nuxt is fully hydrated. You can change this default by modifying the [defaultScriptOptions](/docs/api/nuxt-config#defaultscriptoptions).
-
-## Element Event Triggers
+### Element Events
-The [useScriptTriggerElement](/docs/api/use-script-trigger-element) composable allows you to hook into element events as a way to load script. This is useful for loading scripts when a user interacts with a specific element.
+Use [`useScriptTriggerElement()`{lang="ts"}](/docs/api/use-script-trigger-element){lang="ts"} to trigger scripts based on specific element interactions:
```ts
-const somethingEl = ref()
-const { trigger } = useScript({
- src: 'https://example.com/script.js',
-}, {
+const buttonEl = ref()
+
+useScript('https://example.com/feature.js', {
trigger: useScriptTriggerElement({
- trigger: 'hover',
- somethingEl,
+ trigger: 'visible', // or 'hover', 'click', etc.
+ el: buttonEl,
})
})
```
-It has support for the following triggers:
-- `visible` - Triggered when the element becomes visible in the viewport.
-- `mouseover` - Triggered when the element is hovered over.
+## Basic Triggers
-## Manual Trigger
+### Manual Control
-The `manual` trigger allows you to manually trigger the loading of a script. This gives you complete
-control over when the script is loaded.
+Use `manual` trigger for complete control over script loading:
```ts
const { load } = useScript('https://example.com/script.js', {
trigger: 'manual'
})
-// ...
-load()
-```
-
-## Promise
-You can use a promise to trigger the loading of a script. This is useful for any other custom trigger you might want to use.
-
-```ts
-const myScript = useScript('/script.js', {
- // load after 3 seconds
- trigger: new Promise(resolve => setTimeout(resolve, 3000))
-})
+// Load when you decide
+await load()
```
diff --git a/docs/content/docs/1.guides/1.warmup.md b/docs/content/docs/1.guides/1.warmup.md
new file mode 100644
index 00000000..d50e9189
--- /dev/null
+++ b/docs/content/docs/1.guides/1.warmup.md
@@ -0,0 +1,58 @@
+---
+
+title: Warmup Strategy
+description: Customize the preload or preconnect strategy used for your scripts.
+
+---
+
+## Background
+
+Nuxt Scripts will insert relevant warmup `link` tags to optimize the loading of your scripts. Optimizing
+for the quickest load after Nuxt has finished hydrating.
+
+For example if we have a script like so:
+
+```ts
+useScript('/script.js')
+```
+
+This code will load in `/script.js` on the `onNuxtReady` event. As the network may be idle while your Nuxt App is hydrating,
+Nuxt Scripts will use this time to warmup the script by inserting a `preload` tag in the `head` of the document.
+
+```html
+
+```
+
+This behavior only applies when using the `client` or `onNuxtReady` [Script Triggers](/docs/guides/script-triggers).
+To customize the behavior further, you can use the `warmupStrategy` option.
+
+## `warmupStrategy`
+
+You can use the `warmupStrategy` option to customize the `link` tag inserted for the script. The option can be a function
+that returns an object with the following properties:
+
+* - `false` - Disable warmup.
+* - `'preload'` - Preload the script, use when you load the script immediately.
+* - `'preconnect'` or `'dns-prefetch'` - Preconnect to the script origin, use when you know you will load a script within 10 seconds. Only works when loading a script from a different origin, will fallback to `false` if the origin is the same.
+
+All of these options can also be passed to a callback function, which can be useful when you have a dynamic trigger for the script.
+
+## `warmup`
+
+You can call the `warmup` function explicitly to add preconnect or preload link tags for a script. This only works the first time you call the function.
+
+This can be useful when you know that you are going to load the script shortly.
+
+```ts
+const script = useScript('/video.js', {
+ trigger: 'manual'
+})
+// warmup the script when we think the user may need the script
+onVisible(videoContainer, () => {
+ script.warmup('preload')
+})
+// load it in
+onClick(videoContainer, () => {
+ script.load()
+})
+```
diff --git a/docs/content/docs/1.guides/2.bundling.md b/docs/content/docs/1.guides/2.bundling.md
deleted file mode 100644
index 154f032a..00000000
--- a/docs/content/docs/1.guides/2.bundling.md
+++ /dev/null
@@ -1,120 +0,0 @@
----
-title: Bundling Remote Scripts
-description: Optimize third-party scripts by bundling them with your app.
----
-
-## Background
-
-When you use scripts from other sites on your website, you rely on another server to load these scripts. This can slow down your site and raise concerns about safety and privacy.
-
-### Common problems
-
-- Slower website because it takes time to connect to other servers.
-- Safety risks if the other server is hacked.
-- Your visitors' data being used inappropriately by other servers.
-- Ad blockers or privacy tools might stop these scripts from working.
-
-### How to fix it
-
-By bundling these scripts, you can host them yourself, which helps avoid these issues and keeps your site running smoothly.
-
-## How it Works
-
-During the build process, your code is checked to find any instances of `useScript` that need to be bundled.
-
-When a script is identified for bundling, it's downloaded and saved as a public asset at `/_scripts/[hash].js`. Here, `[hash]` represents the hash of the script's URL.
-
-**Important points about bundling:**
-
-1. You need to have static values for your script URLs and bundling settings.
-
-::code-group
-
-```ts [Input - Pre Build]
-// GOOD - Static values allow for bundling
-useScript('https://example.com/script.js', {
- bundle: true
-})
-// BAD - Dynamic values prevent bundling
-useScript(scriptSrc, {
- bundle: canBundle
-})
-```
-
-```ts [Output - Post Build]
-// GOOD - Script is bundled
-useScript('/_scripts/[hash].js', {})
-// BAD - Script is not bundled (remains the same)
-useScript(scriptSrc, {
- bundle: canBundle
-})
-```
-
-::
-
-2. If the original script changes without a URL change, the bundled version won't update in the browser cache. To handle this, use a versioned URL or a cache-busting query parameter.
-
-## Usage
-
-Scripts can be bundled individually or on a global scale using specific settings.
-
-### Script Options
-
-To decide if an individual script should be bundled, use the `bundle` option.
-
-::code-group
-
-```ts [useScript]
-// Opt-in to bundle this specific script
-useScript('https://example.com/script.js', {
- bundle: true,
-})
-```
-
-```ts [Registry Script]
-// Registry script must support bundling
-useScriptGoogleAnalytics('https://example.com/script.js', {
- bundle: true,
-})
-```
-::
-
-### Global Bundling
-
-Adjust the default behavior for all scripts using the Nuxt Config. This example sets all scripts to be bundled by default.
-
-```ts [nuxt.config.ts]
-export default defineNuxtConfig({
- scripts: {
- defaultScriptOptions: {
- bundle: true,
- }
- }
-})
-```
-
-### Limitations of Bundling
-
-While many scripts can be bundled, there are exceptions you need to be aware of.
-
-For instance, certain scripts:
-- Require tracking all user interactions for security reasons, like fraud detection (e.g., Stripe).
-- Must be served directly from their original source to function properly (e.g., Fathom Analytics).
-
-Scripts from known registries are pre-configured to either allow or disallow bundling. For your own scripts, you'll need to decide whether bundling is appropriate on a case-by-case basis.
-
-### Change Asset Behavior
-
-Use the `assets` option in your configuration to customize how scripts are bundled, such as changing the output directory for the bundled scripts.
-
-```ts [nuxt.config.ts]
-export default defineNuxtConfig({
- scripts: {
- assets: {
- prefix: '/_custom-script-path/',
- }
- }
-})
-```
-
-More configuration options will be available in future updates.
diff --git a/docs/content/docs/1.guides/2.first-party.md b/docs/content/docs/1.guides/2.first-party.md
new file mode 100644
index 00000000..120f58b8
--- /dev/null
+++ b/docs/content/docs/1.guides/2.first-party.md
@@ -0,0 +1,702 @@
+---
+
+title: First-Party Mode
+description: Route third-party script traffic through your domain for improved privacy and reliability.
+
+---
+
+## Background
+
+When third-party scripts load directly from external servers, they expose your users' data:
+
+- **IP address exposure** - Every request reveals your users' IP addresses to third parties
+- **Third-party cookies** - External scripts can set cookies for cross-site tracking
+- **Ad blocker interference** - Privacy tools block requests to known tracking domains
+- **Connection overhead** - Extra DNS lookups and TLS handshakes slow page loads
+
+### How First-Party Mode Helps
+
+First-party mode routes all script traffic through your domain:
+
+- **User IPs anonymized** - IPs are anonymized to subnet level before forwarding; third parties can't identify individual users
+- **Device fingerprinting reduced** - Screen resolution, User-Agent, and hardware info are generalized to common buckets
+- **No third-party cookies** - Requests are same-origin, eliminating cross-site tracking
+- **Works with ad blockers** - Requests appear first-party. Google's [server-side tagging documentation](https://developers.google.com/tag-platform/tag-manager/server-side/intro#benefits) notes that this approach provides more reliable data collection and better control over user privacy.
+- **Faster loads** - No extra DNS lookups for external domains
+
+## How it Works
+
+When you enable first-party mode:
+
+1. **Build time**: Nuxt downloads scripts and rewrites URLs to local paths (e.g., `https://www.google-analytics.com/g/collect` → `/_scripts/c/ga/g/collect`)
+2. **Runtime**: Nitro route rules proxy requests from local paths back to original endpoints
+
+```
+User Browser → Your Server (/_scripts/c/ga/...) → Google Analytics
+```
+
+Your users never connect directly to third-party servers.
+
+## Usage
+
+### Enable Globally
+
+Enable first-party mode for all supported scripts:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: true,
+ registry: {
+ googleAnalytics: { id: 'G-XXXXXX' },
+ metaPixel: { id: '123456' },
+ }
+ }
+})
+```
+
+### Privacy Controls
+
+Each script in the registry declares its own privacy defaults based on what data it needs. Privacy is controlled by six flags:
+
+| Flag | What it does |
+|------|-------------|
+| `ip` | Anonymizes IP addresses to subnet level in headers and payload params |
+| `userAgent` | Normalizes User-Agent to browser family + major version (e.g. `Mozilla/5.0 (compatible; Chrome/131.0)`{lang="ts"}) |
+| `language` | Normalizes Accept-Language to primary language tag |
+| `screen` | Generalizes screen resolution, viewport, hardware concurrency, and device memory to common buckets |
+| `timezone` | Generalizes timezone offset and IANA timezone names |
+| `hardware` | Anonymizes canvas/webgl/audio fingerprints, plugin/font lists, browser versions, and device info |
+
+Sensitive headers (`cookie`, `authorization`) are **always** stripped regardless of privacy settings.
+
+#### Per-Script Defaults
+
+Scripts declare the privacy that makes sense for their use case:
+
+| Script | ip | userAgent | language | screen | timezone | hardware | Rationale |
+|--------|:--:|:---------:|:--------:|:------:|:--------:|:--------:|-----------|
+| Google Analytics | ✓ | - | ✓ | - | - | ✓ | UA/screen/timezone needed for device, time, and OS reports |
+| Google Tag Manager | - | - | - | - | - | - | Container script loading - no user data in requests |
+| Meta Pixel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Untrusted ad network - full anonymization |
+| TikTok Pixel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Untrusted ad network - full anonymization |
+| X/Twitter Pixel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Untrusted ad network - full anonymization |
+| Snapchat Pixel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Untrusted ad network - full anonymization |
+| Reddit Pixel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Untrusted ad network - full anonymization |
+| Segment | - | - | - | - | - | - | Trusted data pipeline - full fidelity required |
+| PostHog | - | - | - | - | - | - | Trusted, open-source - full fidelity required |
+| Microsoft Clarity | ✓ | - | ✓ | - | - | ✓ | UA/screen/timezone needed for heatmaps and device filtering |
+✓ = anonymized, - = passed through
+
+| Hotjar | ✓ | - | ✓ | - | - | ✓ | UA/screen/timezone needed for heatmaps and device filtering |
+
+#### Global Override
+
+Override all per-script defaults at once:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: {
+ privacy: true, // Full anonymize for ALL scripts
+ }
+ }
+})
+```
+
+Or selectively override specific flags:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: {
+ privacy: { ip: true }, // Anonymize IP for all scripts, rest uses per-script defaults
+ }
+ }
+})
+```
+
+::callout{type="info"}
+When a flag is active, data is either **generalized** (reduced precision) or **redacted** (emptied/zeroed) - analytics endpoints still receive valid data. For example, screen resolution `1440x900` becomes `1920x1080` (desktop bucket) and User-Agent is normalized to `Mozilla/5.0 (compatible; Chrome/131.0)`{lang="ts"}, while hardware fingerprints like canvas, WebGL, plugins, and fonts are zeroed or cleared.
+::
+
+### Custom Paths
+
+Customize the proxy endpoint paths:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: {
+ collectPrefix: '/_analytics', // Default: /_scripts/c
+ }
+ }
+})
+```
+
+### Opt-out Per Script
+
+Disable first-party routing for a specific script:
+
+```ts
+useScriptGoogleAnalytics({
+ id: 'G-XXXXXX',
+ scriptOptions: {
+ firstParty: false, // Load directly from Google
+ }
+})
+```
+
+## Supported Scripts
+
+First-party mode supports the following scripts:
+
+| Script | Endpoints Proxied |
+|--------|-------------------|
+| [Google Analytics](/scripts/analytics/google-analytics) | `google-analytics.com`, `analytics.google.com`, `stats.g.doubleclick.net`, `pagead2.googlesyndication.com` |
+| [Google Tag Manager](/scripts/tracking/google-tag-manager) | `www.googletagmanager.com` |
+| [Meta Pixel](/scripts/tracking/meta-pixel) | `connect.facebook.net`, `www.facebook.com/tr`, `pixel.facebook.com` |
+| [TikTok Pixel](/scripts/tracking/tiktok-pixel) | `analytics.tiktok.com` |
+| [Segment](/scripts/tracking/segment) | `api.segment.io`, `cdn.segment.com` |
+| [PostHog](/scripts/analytics/posthog) | `us.i.posthog.com`, `eu.i.posthog.com`, `us-assets.i.posthog.com`, `eu-assets.i.posthog.com` |
+| [Microsoft Clarity](/scripts/marketing/clarity) | `www.clarity.ms`, `scripts.clarity.ms`, `d.clarity.ms`, `e.clarity.ms` |
+| [Hotjar](/scripts/marketing/hotjar) | `static.hotjar.com`, `script.hotjar.com`, `vars.hotjar.com`, `in.hotjar.com` |
+| [X/Twitter Pixel](/scripts/tracking/x-pixel) | `analytics.twitter.com`, `t.co` |
+| [Snapchat Pixel](/scripts/tracking/snapchat-pixel) | `tr.snapchat.com` |
+| [Reddit Pixel](/scripts/tracking/reddit-pixel) | `alb.reddit.com`, `pixel-config.reddit.com` |
+| [Plausible Analytics](/scripts/analytics/plausible-analytics) | `plausible.io` |
+| [Cloudflare Web Analytics](/scripts/analytics/cloudflare-web-analytics) | `static.cloudflareinsights.com`, `cloudflareinsights.com` |
+| [Rybbit Analytics](/scripts/analytics/rybbit-analytics) | `app.rybbit.io` |
+| [Umami Analytics](/scripts/analytics/umami-analytics) | `cloud.umami.is` |
+| [Databuddy Analytics](/scripts/analytics/databuddy-analytics) | `cdn.databuddy.cc`, `basket.databuddy.cc` |
+| [Fathom Analytics](/scripts/analytics/fathom-analytics) | `cdn.usefathom.com` |
+| [Vercel Analytics](/scripts/analytics/vercel-analytics) | `va.vercel-scripts.com` |
+| [Intercom](/scripts/support/intercom) | `widget.intercom.io`, `api-iam.intercom.io` |
+| [Crisp](/scripts/support/crisp) | `client.crisp.chat` |
+
+## Requirements
+
+First-party mode requires a **server runtime**. It won't work with fully static hosting (e.g., `nuxt generate` to GitHub Pages) because the proxy endpoints need a server to forward requests.
+
+For static deployments, you can still enable first-party mode - Nuxt bundles scripts with rewritten URLs, but you'll need to configure your hosting platform's rewrite rules manually.
+
+### Static Hosting Rewrites
+
+If deploying statically, configure your platform to proxy these paths:
+
+```
+/_scripts/c/ga/* → https://www.google.com/*
+/_scripts/c/gtm/* → https://www.googletagmanager.com/*
+/_scripts/c/meta/* → https://connect.facebook.net/*
+```
+
+## First-Party vs Bundle
+
+First-party mode supersedes the `bundle` option:
+
+| Feature | `bundle: true` | `firstParty: true` |
+|---------|---------------|-------------------|
+| Downloads script at build | ✅ | ✅ |
+| Serves from your domain | ✅ | ✅ |
+| Rewrites collection URLs | ❌ | ✅ |
+| Proxies API requests | ❌ | ✅ |
+| Hides user IPs | ❌ | ✅ |
+| Blocks third-party cookies | ❌ | ✅ |
+
+The `bundle` option only self-hosts the script file. First-party mode also rewrites and proxies all collection/tracking endpoints, providing complete first-party routing.
+
+::callout{type="warning"}
+The `bundle` option is deprecated. Use `firstParty: true` for new projects.
+::
+
+## Default Behavior
+
+First-party mode is **enabled by default**. For static hosting (GitHub Pages, etc.), Nuxt still bundles scripts but you'll need to configure platform rewrites for the proxy endpoints to work.
+
+To disable first-party mode:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: false
+ }
+})
+```
+
+## Platform Rewrites
+
+When deploying to static hosting or edge platforms, configure these rewrites to proxy collection endpoints.
+
+### Vercel
+
+```json [vercel.json]
+{
+ "rewrites": [
+ { "source": "/_scripts/c/ga/:path*", "destination": "https://www.google.com/:path*" },
+ { "source": "/_scripts/c/ga-legacy/:path*", "destination": "https://www.google-analytics.com/:path*" },
+ { "source": "/_scripts/c/gtm/:path*", "destination": "https://www.googletagmanager.com/:path*" },
+ { "source": "/_scripts/c/meta/:path*", "destination": "https://connect.facebook.net/:path*" },
+ { "source": "/_scripts/c/tiktok/:path*", "destination": "https://analytics.tiktok.com/:path*" },
+ { "source": "/_scripts/c/segment/:path*", "destination": "https://api.segment.io/:path*" },
+ { "source": "/_scripts/c/clarity/:path*", "destination": "https://www.clarity.ms/:path*" },
+ { "source": "/_scripts/c/hotjar/:path*", "destination": "https://static.hotjar.com/:path*" },
+ { "source": "/_scripts/c/x/:path*", "destination": "https://analytics.twitter.com/:path*" },
+ { "source": "/_scripts/c/snap/:path*", "destination": "https://tr.snapchat.com/:path*" },
+ { "source": "/_scripts/c/reddit/:path*", "destination": "https://alb.reddit.com/:path*" }
+ ]
+}
+```
+
+### Netlify
+
+```toml [netlify.toml]
+[[redirects]]
+from = "/_scripts/c/ga/*"
+to = "https://www.google.com/:splat"
+status = 200
+
+[[redirects]]
+from = "/_scripts/c/ga-legacy/*"
+to = "https://www.google-analytics.com/:splat"
+status = 200
+
+[[redirects]]
+from = "/_scripts/c/gtm/*"
+to = "https://www.googletagmanager.com/:splat"
+status = 200
+
+[[redirects]]
+from = "/_scripts/c/meta/*"
+to = "https://connect.facebook.net/:splat"
+status = 200
+
+# Add more as needed...
+```
+
+### Cloudflare Pages
+
+Create a `_redirects` file in your public directory:
+
+```txt [public/_redirects]
+/_scripts/c/ga/* https://www.google.com/:splat 200
+/_scripts/c/ga-legacy/* https://www.google-analytics.com/:splat 200
+/_scripts/c/gtm/* https://www.googletagmanager.com/:splat 200
+/_scripts/c/meta/* https://connect.facebook.net/:splat 200
+```
+
+Or use Cloudflare Workers for more control:
+
+```ts [functions/_scripts/c/[[path]].ts]
+export const onRequest: PagesFunction = async (context) => {
+ const url = new URL(context.request.url)
+ const path = url.pathname.replace('/_scripts/c/', '')
+
+ // Route based on prefix
+ const routes: Record = {
+ 'ga/': 'https://www.google.com/',
+ 'gtm/': 'https://www.googletagmanager.com/',
+ 'meta/': 'https://connect.facebook.net/',
+ }
+
+ for (const [prefix, target] of Object.entries(routes)) {
+ if (path.startsWith(prefix)) {
+ const targetUrl = target + path.slice(prefix.length) + url.search
+ return fetch(targetUrl, context.request)
+ }
+ }
+
+ return new Response('Not found', { status: 404 })
+}
+```
+
+## Architecture Diagram
+
+```
+┌─────────────────────────────────────────────────────────────────────┐
+│ BUILD TIME │
+├─────────────────────────────────────────────────────────────────────┤
+│ │
+│ 1. Download script from third-party │
+│ https://www.googletagmanager.com/gtag/js?id=G-XXX │
+│ ↓ │
+│ 2. Rewrite URLs in script content │
+│ "www.google.com/g/collect" → "/_scripts/c/ga/g/collect" │
+│ ↓ │
+│ 3. Save rewritten script to build output │
+│ .output/public/_scripts/abc123.js │
+│ │
+└─────────────────────────────────────────────────────────────────────┘
+ ↓
+┌─────────────────────────────────────────────────────────────────────┐
+│ RUNTIME │
+├─────────────────────────────────────────────────────────────────────┤
+│ │
+│ User Browser │
+│ │ │
+│ │ 1. Request script │
+│ │ GET /_scripts/abc123.js │
+│ ↓ │
+│ Your Server (Nitro) │
+│ │ │
+│ │ 2. Serve bundled script (rewritten URLs) │
+│ ↓ │
+│ User Browser │
+│ │ │
+│ │ 3. Script sends analytics │
+│ │ POST /_scripts/c/ga/g/collect │
+│ ↓ │
+│ Your Server (Nitro route rule) │
+│ │ │
+│ │ 4. Proxy to third-party │
+│ │ POST https://www.google.com/g/collect │
+│ ↓ │
+│ Third-Party Server │
+│ │ │
+│ │ 5. Sees YOUR server's IP, not user's IP │
+│ ↓ │
+│ Response proxied back to user │
+│ │
+└─────────────────────────────────────────────────────────────────────┘
+```
+
+## Troubleshooting
+
+### Analytics Not Tracking
+
+**Symptoms**: Events not appearing in analytics dashboard.
+
+**Check these:**
+
+1. **Verify proxy routes are active** - In dev mode, check `/_scripts/status.json` or the DevTools Scripts panel
+2. **Check browser Network tab** - Look for requests to `/_scripts/c/` paths and verify they return 200
+3. **Check server logs** - Look for proxy errors in Nitro logs
+4. **Verify route rules** - Run `npx nuxi build` and check `.output/server/chunks/routes/rules.mjs`
+
+```ts
+// Debug: Log all requests in server middleware
+export default defineEventHandler((event) => {
+ if (event.path.startsWith('/_scripts/c/')) {
+ console.log('Proxying:', event.path)
+ }
+})
+```
+
+### CORS Errors
+
+**Symptoms**: Browser console shows CORS errors for analytics requests.
+
+**Solutions:**
+
+1. Ensure you correctly configure route rules in `nuxt.config.ts`
+2. For static hosting, verify platform rewrites are set up
+3. Check that the target URLs in rewrites match exactly
+
+### Cached Old Script
+
+**Symptoms**: Script content is stale or URLs aren't rewritten.
+
+**Solutions:**
+
+1. Clear the build cache:
+ ```bash
+ rm -rf .nuxt/cache/scripts
+ npx nuxi build
+ ```
+
+2. Force re-download during development:
+ ```ts
+ useScriptGoogleAnalytics({
+ id: 'G-XXX',
+ scriptOptions: { bundle: 'force' }
+ })
+ ```
+
+### Static Build Not Proxying
+
+**Symptoms**: Scripts load but analytics don't track on static hosting.
+
+This behavior is normal - Static builds cannot proxy requests. Configure platform rewrites (see Platform Rewrites section above) or switch to server-rendered mode.
+
+### Script Download Fails
+
+**Symptoms**: Build fails with network timeout errors.
+
+**Solutions:**
+
+1. Enable fallback mode:
+ ```ts [nuxt.config.ts]
+ export default defineNuxtConfig({
+ scripts: {
+ firstParty: true,
+ assets: {
+ fallbackOnSrcOnBundleFail: true
+ }
+ }
+ })
+ ```
+
+2. Increase timeout:
+ ```ts [nuxt.config.ts]
+ export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ fetchOptions: {
+ timeout: 30000 // 30 seconds
+ }
+ }
+ }
+ })
+ ```
+
+## FAQ
+
+### Does first-party mode bypass GDPR consent requirements?
+
+**No.** First-party mode changes where requests are routed, not whether tracking occurs. You still need user consent before loading tracking scripts. Use consent triggers:
+
+```ts
+const { accept } = useScriptTriggerConsent()
+
+// Only load after user consents
+function onConsentGiven() {
+ accept()
+}
+```
+
+### Will analytics still work if the third-party changes their script?
+
+**Yes, with automatic updates.** Nuxt caches scripts for 7 days by default. When the cache expires, the script is re-downloaded and URLs are rewritten again. You can customize cache duration:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ cacheMaxAge: 86400000 // 1 day in ms
+ }
+ }
+})
+```
+
+### Can I customize proxy paths?
+
+**Yes.** Use the `collectPrefix` option:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: {
+ collectPrefix: '/_tracking' // Instead of /_scripts/c
+ }
+ }
+})
+```
+
+### Does this work with Server-Side Tracking?
+
+First-party mode is for client-side scripts. For server-side tracking (Measurement Protocol, etc.), send requests directly from your server endpoints without the proxy layer.
+
+### How do I debug what's being proxied?
+
+1. **DevTools**: Open Nuxt DevTools → Scripts → First-Party tab
+2. **Status endpoint**: Visit `/_scripts/status.json` in dev mode
+3. **Console logs**: Enable debug mode:
+ ```ts [nuxt.config.ts]
+ export default defineNuxtConfig({
+ scripts: {
+ debug: true
+ }
+ })
+ ```
+
+### Is there a performance impact?
+
+**Minimal.** The proxy adds a small latency (~10-50ms) as requests go through your server. However, this is often offset by:
+- Eliminating DNS lookups for third-party domains
+- Bypassing ad blockers that would otherwise block requests
+- Reduced connection overhead (reusing existing connections)
+
+### Which scripts can I add first-party support to?
+
+Currently, first-party mode supports all scripts listed in the Supported Scripts section. For other scripts, you can:
+
+1. Request support by opening an issue
+2. Use the `bundle` option for self-hosting without proxy (deprecated)
+3. Configure custom route rules manually
+
+## Hybrid Rendering
+
+First-party mode works with Nuxt's hybrid rendering features:
+
+### Route-Level SSR
+
+When using `routeRules` to disable SSR for specific routes, first-party mode still works because:
+
+1. Nuxt bundles scripts at build time (not runtime)
+2. Proxy route rules are global Nitro configuration
+3. Collection requests still go through your server
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ routeRules: {
+ '/dashboard/**': { ssr: false }, // SPA mode for dashboard
+ },
+ scripts: {
+ firstParty: true, // Still works for all routes
+ }
+})
+```
+
+### ISR (Incremental Static Regeneration)
+
+ISR pages work with first-party mode. The bundled scripts are served from static assets, while collection requests are proxied through Nitro at runtime.
+
+### Edge Rendering
+
+First-party mode works with edge deployments (Cloudflare Workers, Vercel Edge, etc.). The proxy routes use Nitro's native proxy support which works across all deployment targets.
+
+::callout{type="info"}
+For edge deployments, ensure your edge runtime supports outbound fetch requests to the analytics endpoints.
+::
+
+## Consent Integration
+
+First-party mode and consent management are complementary features:
+
+- **First-party mode** controls *where* requests go (through your server vs direct)
+- **Consent triggers** control *when* scripts load (before vs after consent)
+
+### Using Both Together
+
+```vue
+
+```
+
+### Consent Banner Example
+
+```vue
+
+
+
+
+
We use analytics to improve your experience.
+
+ Accept
+
+
+ Reject
+
+
+
+```
+
+### Privacy Flow
+
+```
+User visits site
+ │
+ ↓
+Scripts registered but NOT loaded (consent pending)
+ │
+ ↓
+User accepts cookies
+ │
+ ↓
+Scripts load from YOUR server (/_scripts/...)
+ │
+ ↓
+Analytics sent through YOUR server (/_scripts/c/...)
+ │
+ ↓
+Third-party sees YOUR server's IP, not user's
+```
+
+This gives you both GDPR compliance (consent before tracking) and enhanced privacy (first-party routing after consent).
+
+## Health Check
+
+To verify first-party mode is working correctly:
+
+### 1. Check DevTools
+
+Open Nuxt DevTools → Scripts → First-Party tab to see:
+- Enabled status
+- Configured scripts
+- Active proxy routes
+
+### 2. Check Status Endpoint
+
+In development, visit `/_scripts/status.json`:
+
+```json
+{
+ "enabled": true,
+ "scripts": ["googleAnalytics", "metaPixel"],
+ "routes": {
+ "/_scripts/c/ga/**": "https://www.google.com/**",
+ "/_scripts/c/meta/**": "https://connect.facebook.net/**"
+ },
+ "collectPrefix": "/_scripts/c"
+}
+```
+
+### 3. Verify in Browser
+
+1. Open browser DevTools → Network tab
+2. Filter by `/_scripts`
+3. Trigger an analytics event
+4. Verify requests go to `/_scripts/c/...` paths (not third-party domains)
+5. Check response status is 200
+
+### 4. CLI Status
+
+Run the CLI command to check cache status:
+
+```bash
+npx nuxt-scripts status
+```
+
+This shows cached scripts and their sizes.
diff --git a/docs/content/docs/1.guides/3.bundling.md b/docs/content/docs/1.guides/3.bundling.md
new file mode 100644
index 00000000..7b985bcf
--- /dev/null
+++ b/docs/content/docs/1.guides/3.bundling.md
@@ -0,0 +1,336 @@
+---
+
+title: Bundling Remote Scripts
+description: Optimize third-party scripts by bundling them with your app.
+
+---
+
+::callout{type="warning"}
+The `bundle` option is deprecated in favor of [First-Party Mode](/docs/guides/first-party), which provides the same benefits plus routed collection endpoints for improved privacy. Use `firstParty: true` for new projects.
+::
+
+## Background
+
+When you use scripts from other sites on your website, you rely on another server to load these scripts. This can slow down your site and raise concerns about safety and privacy.
+
+### Common problems
+
+- Slower website because it takes time to connect to other servers.
+- Safety risks if the other server is hacked.
+- Other servers may use your visitors' data inappropriately.
+- Ad blockers or privacy tools might stop these scripts from working.
+
+### How to fix it
+
+By bundling these scripts, you can host them yourself, which helps avoid these issues and keeps your site running smoothly.
+
+## How it Works
+
+During the build process, Nuxt checks your code to find any instances of [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} that need bundling.
+
+When Nuxt identifies a script for bundling, it downloads and saves it as a public asset at `/_scripts/[hash].js`. Here, `[hash]` represents the hash of the script's URL.
+
+**Important points about bundling:**
+
+1. You need to have static values for your script URLs and bundling settings.
+
+::code-group
+
+```ts [Input - Pre Build]
+// GOOD - Static values allow for bundling
+useScript('https://example.com/script.js', {
+ bundle: true
+})
+// BAD - Dynamic values prevent bundling
+useScript(scriptSrc, {
+ bundle: canBundle
+})
+```
+
+```ts [Output - Post Build]
+// GOOD - Nuxt bundles the script
+useScript('/_scripts/[hash].js', {})
+// BAD - Nuxt does not bundle the script (remains the same)
+useScript(scriptSrc, {
+ bundle: canBundle
+})
+```
+
+::
+
+2. If the original script changes without a URL change, the bundled version won't update in the browser cache. To handle this, use a versioned URL or a cache-busting query parameter.
+
+## Usage
+
+You can bundle scripts individually or on a global scale using specific settings.
+
+### Script Options
+
+To decide if Nuxt can bundle an individual script, use the `bundle` option.
+
+::code-group
+
+```ts [useScript]
+// Opt-in to bundle this specific script
+useScript('https://example.com/script.js', {
+ bundle: true,
+})
+
+// Force download bypassing cache
+useScript('https://example.com/script.js', {
+ bundle: 'force',
+})
+```
+
+```ts [Registry Script]
+// Registry script bundling using scriptOptions
+useScriptGoogleAnalytics({
+ id: 'GA_MEASUREMENT_ID',
+ scriptOptions: {
+ bundle: true
+ }
+})
+
+// bundle without cache
+useScriptGoogleAnalytics({
+ id: 'GA_MEASUREMENT_ID',
+ scriptOptions: {
+ bundle: 'force'
+ }
+})
+```
+::
+
+#### Bundle Options
+
+The `bundle` option accepts the following values:
+
+- `false` - Do not bundle the script (default)
+- `true` - Bundle the script and use cached version if available
+- `'force'` - Bundle the script and force download, bypassing cache
+
+**Note**: Using `'force'` will re-download scripts on every build, which may increase build time and provide less security.
+
+### Global Bundling
+
+Adjust the default behavior for all scripts using the Nuxt Config. This example sets Nuxt to bundle all scripts by default.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ defaultScriptOptions: {
+ bundle: true,
+ }
+ }
+})
+```
+
+### Build-time vs Runtime Behavior
+
+Understanding when bundling happens and how it affects runtime behavior is crucial for effective usage.
+
+#### Build-time Processing
+
+Bundling occurs during the build phase through static code analysis:
+
+```ts
+// ✅ Nuxt bundles at build-time (static values)
+useScript('https://example.com/script.js', { bundle: true })
+
+// ❌ Nuxt cannot bundle (dynamic values)
+const scriptUrl = computed(() => getScriptUrl())
+useScript(scriptUrl, { bundle: dynamic.value })
+```
+
+#### Runtime Behavior
+
+At runtime, bundled scripts behave differently:
+
+```ts
+// Original code
+useScript('https://example.com/script.js', { bundle: true })
+
+// After build transformation
+useScript('/_scripts/abc123.js', {})
+```
+
+**Important**: Once Nuxt bundles the script, you lose access to the original URL at runtime. If you need the original URL for tracking or analytics, store it separately.
+
+#### Static URL Requirements
+
+For bundling to work, the transformer requires **static values**:
+
+::code-group
+
+```ts [✅ Valid for Bundling]
+// Static string literals
+useScript('https://cdn.example.com/lib.js', { bundle: true })
+
+// Static template literals (no variables)
+useScript(`https://cdn.example.com/lib.js`, { bundle: true })
+
+// Constants defined at module level
+const SCRIPT_URL = 'https://cdn.example.com/lib.js'
+useScript(SCRIPT_URL, { bundle: true })
+```
+
+```ts [❌ Nuxt Cannot Bundle]
+// Runtime variables
+const url = getScriptUrl()
+useScript(url, { bundle: true })
+
+// Computed values
+const scriptUrl = computed(() => `https://cdn.example.com/${version.value}.js`)
+useScript(scriptUrl, { bundle: true })
+
+// Environment variables at runtime
+useScript(process.env.SCRIPT_URL, { bundle: true })
+
+// Props or reactive values
+useScript(props.scriptUrl, { bundle: true })
+```
+
+::
+
+#### Manual Injection Patterns
+
+When automatic bundling isn't possible, you can manually inject bundled scripts:
+
+```ts [Manual Bundling Workaround]
+// 1. Bundle during build with static URL
+const staticScript = useScript('https://cdn.example.com/static.js', {
+ bundle: true,
+ trigger: 'manual' // Don't auto-load
+})
+
+// 2. Conditionally load based on runtime logic
+function loadScript() {
+ if (shouldLoadScript.value) {
+ staticScript.load()
+ }
+}
+
+// 3. Alternative: Use multiple static configurations
+const scriptVariants = {
+ dev: useScript('https://cdn.example.com/dev.js', { bundle: true, trigger: 'manual' }),
+ prod: useScript('https://cdn.example.com/prod.js', { bundle: true, trigger: 'manual' })
+}
+
+// Load appropriate variant
+const currentScript = computed(() =>
+ isDev ? scriptVariants.dev : scriptVariants.prod
+)
+```
+
+#### Working with Dynamic URLs
+
+For truly dynamic scenarios, consider these patterns:
+
+```ts [Dynamic URL Strategies]
+// Option 1: Pre-bundle known variants
+const analytics = {
+ google: useScript('https://www.googletagmanager.com/gtag/js', { bundle: true }),
+ plausible: useScript('https://plausible.io/js/script.js', { bundle: true })
+}
+
+// Option 2: Fallback to runtime loading
+function loadDynamicScript(url: string) {
+ // Nuxt won't bundle this, but it will work at runtime
+ return useScript(url, {
+ bundle: false, // Explicitly disable
+ trigger: 'manual'
+ })
+}
+
+// Option 3: Use server-side bundling
+// Store script content in your bundle and inject manually
+const { $script } = useNuxtApp()
+$script.add({
+ innerHTML: await $fetch('/api/dynamic-script-content'),
+})
+```
+
+### Asset Configuration
+
+Use the `assets` option in your configuration to customize how Nuxt bundles and caches scripts.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ prefix: '/_custom-script-path/',
+ cacheMaxAge: 86400000, // 1 day in milliseconds
+ integrity: true, // Enable SRI hash generation
+ }
+ }
+})
+```
+
+#### Available Options
+
+- **`prefix`** - Custom path where Nuxt serves bundled scripts (default: `/_scripts/`)
+- **`cacheMaxAge`** - Cache duration for bundled scripts in milliseconds (default: 7 days)
+- **`integrity`** - Enable automatic SRI (Subresource Integrity) hash generation (default: `false`)
+
+#### Cache Behavior
+
+The bundling system uses two different cache strategies:
+
+- **Build-time cache**: Controlled by `cacheMaxAge` (default: 7 days). Nuxt re-downloads scripts older than this during builds to ensure freshness.
+- **Runtime cache**: Nuxt serves bundled scripts with 1-year cache headers since they are content-addressed by hash.
+
+This dual approach ensures both build performance and reliable browser caching.
+
+### Subresource Integrity (SRI)
+
+Subresource Integrity (SRI) is a security feature that ensures scripts haven't been tampered with. According to the [W3C SRI specification](https://www.w3.org/TR/SRI/), it allows browsers to verify that resources fetched are delivered without unexpected manipulation. When enabled, Nuxt calculates a cryptographic hash for each bundled script and adds it as an `integrity` attribute.
+
+#### Enabling SRI
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ integrity: true, // Uses sha384 by default
+ }
+ }
+})
+```
+
+#### Hash Algorithms
+
+You can specify the hash algorithm:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ integrity: 'sha384', // Default, recommended balance of security/size
+ // integrity: 'sha256', // Smaller hash
+ // integrity: 'sha512', // Strongest security
+ }
+ }
+})
+```
+
+#### How It Works
+
+When you enable integrity:
+
+1. During build, Nuxt hashes each bundled script's content
+2. Nuxt stores the hash in the build cache for reuse
+3. Nuxt injects the `integrity` attribute into the script tag
+4. Nuxt automatically adds the `crossorigin="anonymous"` attribute (required by browsers for SRI)
+
+```html
+
+
+```
+
+#### Security Benefits
+
+- **Tamper detection**: Browser refuses to execute scripts if the hash doesn't match
+- **CDN compromise protection**: Even if your CDN is compromised, modified scripts won't execute
+- **Build-time verification**: Nuxt calculates the hash from the actual downloaded content
diff --git a/docs/content/docs/1.guides/3.consent.md b/docs/content/docs/1.guides/3.consent.md
index c247750e..b7b2f808 100644
--- a/docs/content/docs/1.guides/3.consent.md
+++ b/docs/content/docs/1.guides/3.consent.md
@@ -1,21 +1,27 @@
---
+
title: Consent Management
description: Learn how to get user consent before loading scripts.
+
---
+::callout{icon="i-heroicons-play" to="https://stackblitz.com/github/nuxt/scripts/tree/main/examples/cookie-consent" target="_blank"}
+Try the live [Cookie Consent Example](https://stackblitz.com/github/nuxt/scripts/tree/main/examples/cookie-consent) or [Granular Consent Example](https://stackblitz.com/github/nuxt/scripts/tree/main/examples/granular-consent) on [StackBlitz](https://stackblitz.com).
+::
+
## Background
-Many third-party scripts include tracking cookies that require user consent under privacy laws. Nuxt Scripts simplifies this process with the [useScriptTriggerConsent](/docs/api/use-script-trigger-consent) composable, allowing scripts to be loaded only after receiving user consent.
+Many third-party scripts include tracking cookies that require user consent under privacy laws. Nuxt Scripts simplifies this process with the [`useScriptTriggerConsent()`{lang="ts"}](/docs/api/use-script-trigger-consent){lang="ts"} composable, allowing scripts to load only after you receive user consent.
## Usage
-The [useScriptTriggerConsent](/docs/api/use-script-trigger-consent) composable offers flexible interaction options suitable for various scenarios.
+The [`useScriptTriggerConsent()`{lang="ts"}](/docs/api/use-script-trigger-consent){lang="ts"} composable offers flexible interaction options suitable for various scenarios.
See the [API](/docs/api/use-script-trigger-consent) docs for full details on the available options.
### Accepting as a Function
-The easiest way to make use of `useScriptTriggerConsent` is by invoking the `accept` method when user consent is granted.
+The easiest way to make use of [`useScriptTriggerConsent()`{lang="ts"}](/docs/api/use-script-trigger-consent){lang="ts"} is by invoking the `accept` method when user consent is granted.
For an example of how you might lay out your code to handle this, see the following:
@@ -51,7 +57,7 @@ import { agreedToCookiesScriptConsent } from '#imports'
### Accepting as a resolvable boolean
-Alternatively, you can pass a reactive reference to the consent state to the `useScriptTriggerConsent` composable. This will automatically load the script when the consent state is `true`.
+Alternatively, you can pass a reactive reference to the consent state to the [`useScriptTriggerConsent()`{lang="ts"}](/docs/api/use-script-trigger-consent){lang="ts"} composable. This will automatically load the script when the consent state is `true`.
```ts
const agreedToCookies = ref(false)
@@ -66,7 +72,7 @@ useScript('https://www.google-analytics.com/analytics.js', {
There may be instances where you want to trigger the script load after a certain event, only if the user has consented.
-For this you can use the `postConsentTrigger`, which shares the same API as `trigger` from the `useScript` composable.
+For this you can use the `postConsentTrigger`, which shares the same API as `trigger` from the [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} composable.
```ts
const agreedToCookies = ref(false)
@@ -74,7 +80,9 @@ useScript('https://www.google-analytics.com/analytics.js', {
trigger: useScriptTriggerConsent({
consent: agreedToCookies,
// load 3 seconds after consent is granted
- postConsentTrigger: new Promise(resolve => setTimeout(resolve, 3000))
+ postConsentTrigger: () => new Promise(resolve =>
+ setTimeout(resolve, 3000),
+ ),
})
})
```
diff --git a/docs/content/docs/1.guides/3.page-events.md b/docs/content/docs/1.guides/3.page-events.md
index c7dad1a9..830237af 100644
--- a/docs/content/docs/1.guides/3.page-events.md
+++ b/docs/content/docs/1.guides/3.page-events.md
@@ -1,6 +1,8 @@
---
+
title: Script Event Page
description: Learn how to send page events to your analytics provider.
+
---
## Background
@@ -8,13 +10,13 @@ description: Learn how to send page events to your analytics provider.
When using tracking scripts, it's common to send an event when the page changes. Due to Nuxt's head implementation being
async, the page title is not always available on route change immediately.
-Nuxt Scripts provides the [useScriptEventPage](/docs/api/use-script-event-page) composable to solve this problem.
+Nuxt Scripts provides the [`useScriptEventPage()`{lang="ts"}](/docs/api/use-script-event-page){lang="ts"} composable to solve this problem.
See the [API](/docs/api/use-script-event-page) docs for full details on the available options.
### Usage
-The composable works by providing a function that will be invoked whenever the page changes, providing the newly resolved
+The composable works by providing a function that Nuxt invokes whenever the page changes, providing the newly resolved
title and path.
You can use this with any analytics provider where you're seeing the page title not being accurate on route change.
diff --git a/docs/content/docs/1.guides/4.global.md b/docs/content/docs/1.guides/4.global.md
index 562faab6..e5926e8a 100644
--- a/docs/content/docs/1.guides/4.global.md
+++ b/docs/content/docs/1.guides/4.global.md
@@ -1,6 +1,8 @@
---
+
title: Global Scripts
description: Load global third-party scripts and optimize them for your Nuxt app.
+
---
## Background
@@ -10,7 +12,7 @@ While the `app.head` method in Nuxt Config allows for loading global scripts, it
```ts
export default defineNuxtConfig({
head: {
- script: [ { src: 'https://analytics.com/tracker.js', async: true } ]
+ script: [{ src: 'https://analytics.com/tracker.js', async: true }]
}
})
```
@@ -22,13 +24,13 @@ You may consider using global scripts when:
- You don't care about interacting with the API provided by the third-party script (e.g. you don't need to use `gtag` from Google Analytics).
- You are interacting with the API provided by the third-party script, but you don't care about type safety.
-Otherwise, it's recommended to use [useScript](/docs/api/use-script) to load scripts in a safer way.
+Otherwise, it's recommended to use [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} to load scripts in a safer way.
## Usage
The `globals` key supports strings, objects and arrays.
-**Example: Load a script using just the src**
+**Example: Load a script using the src**
```ts
export default defineNuxtConfig({
@@ -74,7 +76,7 @@ export default defineNuxtConfig({
### Accessing a global script
-All Nuxt Scripts are registered on the `$scripts` Nuxt App property.
+All Nuxt Scripts register on the `$scripts` Nuxt App property.
For scripts registered through nuxt.config, type autocompletion is available.
@@ -87,9 +89,9 @@ $scripts.myScript // { $script, instance }
## How it Works
-The `globals` configuration will be used to create a virtual Nuxt plugin that loads in the script using the `useScript` composable.
+Nuxt uses the `globals` configuration to create a virtual Nuxt plugin that loads in the script using the [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} composable.
-As `useScript` is being used under the hood, it's important to understand the defaults and behavior of the [useScript](/api/use-script) composable..
+As Nuxt uses [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} under the hood, it's important to understand the defaults and behavior of the [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} composable..
::code-group
diff --git a/docs/content/docs/1.guides/5.facade-components.md b/docs/content/docs/1.guides/5.facade-components.md
index 7a3d17c2..8f120c43 100644
--- a/docs/content/docs/1.guides/5.facade-components.md
+++ b/docs/content/docs/1.guides/5.facade-components.md
@@ -1,9 +1,11 @@
---
+
title: Facade Components
description: Facade Components are fake UI elements that get replaced once a third-party script loads.
+
---
-Nuxt Scripts provides several Facade Components that you can use to speed up your app's performance.
+Nuxt Scripts provides several Facade Components that you can use to speed up your app's performance.
Using them has trade-offs, but they can aid in improving the performance experience of your app.
@@ -12,7 +14,7 @@ Using them has trade-offs, but they can aid in improving the performance experie
To render complex components using third-party scripts such as a Video embed, payment modal, or chat widget, we need many resources.
Loading these while Nuxt is starting will slow down your app's performance.
-However, if we delay loading the script until Nuxt is finished, we end up with harmful content layout shifts (CLS) and visual noise,
+However, if we delay loading the script until Nuxt finishes, we end up with harmful content layout shifts (CLS) and visual noise,
leading to a poor user experience.
Facade Components aim to solve this by rendering a "fake" UI element that gets replaced once the third-party script loads.
@@ -21,17 +23,17 @@ By hooking into appropriate DOM events and providing user feedback, we can use t
## What are the trade-offs in using Facade Components?
-While the performance benefit is quite clear, there can be trade-off on user experience.
+While the performance benefit is clear, there can be trade-off on user experience.
-- **Flash of mismatched content**: The fake UI element may not look like the final UI element, leading to a flash of mis-matched content. Only minimal
-styling is provided by Nuxt Scripts, you may need to tweak it to match your app's design.
+- **Flash of mismatched content**: The fake UI element may not look like the final UI element, leading to a flash of mis-matched content. Nuxt Scripts provides only minimal
+styling, you may need to tweak it to match your app's design.
- **Interactivity can break**: Interactivity of the elements requires the script to load, if it doesn't load then you need to provide a fallback.
- **Accessibility Concerns**: We need to provide clear a11y feedback to the user when the script is loading or fails to load.
## Nuxt Scripts Facade Components
-All Facade Components are headless components that wrap the relevant `useScript` composable. Minimal styling is
-provided within the docs to give you a starting point.
+All Facade Components are headless components that wrap the relevant `useScript`{lang="html"} composable. Nuxt provides minimal styling
+within the docs to give you a starting point.
## Best Practices in using Facade Components
@@ -51,7 +53,7 @@ If the script fails to load, provide a fallback that informs the user of the fai
When the script is loading, provide a loading state that informs the user that the content is loading.
-The `ScriptLoadingIndicator` component is provided by Nuxt Scripts to help with this and provides a11y feedback.
+Nuxt Scripts provides the ScriptLoadingIndicator component to help with this and provides a11y feedback.
```vue
@@ -110,9 +112,9 @@ The component provides minimal UI by default, only enough to be functional and a
-
- Click to play!
-
+
+ Click to play!
+
@@ -132,5 +134,5 @@ The component provides minimal UI by default, only enough to be functional and a
### Events
-- `ready` - Emitted when the script has loaded. Gives you access to the underlying script API.
-- `error` - Emitted when the script fails to load.
+- `ready` - Emitted when the script has loaded. Gives you access to the basic script API.
+- `error` - Emitted when the script fails to load.
diff --git a/docs/content/docs/1.guides/6.cors.md b/docs/content/docs/1.guides/6.cors.md
new file mode 100644
index 00000000..00081ab3
--- /dev/null
+++ b/docs/content/docs/1.guides/6.cors.md
@@ -0,0 +1,170 @@
+---
+
+title: CORS and Security Attributes
+description: Understanding how Nuxt Scripts handles cross-origin security.
+
+---
+
+## Background
+
+When loading scripts from external domains, browsers enforce Cross-Origin Resource Sharing (CORS) policies. CORS controls how resources on one domain can be requested by scripts running on another domain. For third-party scripts, this affects:
+
+- Whether the browser sends cookies with requests
+- Access to error details for debugging
+- Subresource Integrity (SRI) validation
+
+## Default Behavior
+
+Nuxt Scripts applies privacy-focused defaults to all scripts:
+
+```html
+
+```
+
+These defaults:
+- **`crossorigin="anonymous"`** - Prevents the script from sending cookies to third-party servers
+- **`referrerpolicy="no-referrer"`** - Prevents sharing the page URL with third-party servers
+
+This improves user privacy but may break scripts that require cookies or referrer information.
+
+## Common CORS Errors
+
+### Script Fails to Load
+
+```text
+Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource
+```
+
+This occurs when a server doesn't return proper CORS headers but `crossorigin="anonymous"` is set. Some third-party scripts don't support CORS.
+
+### Script Loads but Functions Fail
+
+The script loads but functionality is broken because it expected cookies or session data.
+
+### Error Details Hidden
+
+```js
+window.onerror = msg => console.log(msg)
+// Shows: "Script error." instead of actual error
+```
+
+Without `crossorigin`, browsers hide error details from external scripts for security.
+
+## Configuring CORS Attributes
+
+### Per-Script Configuration
+
+Disable CORS attributes for scripts that don't support them:
+
+```ts
+useScript({
+ src: 'https://example.com/script.js',
+ crossorigin: false, // Remove crossorigin attribute
+ referrerpolicy: false, // Remove referrerpolicy attribute
+})
+```
+
+Or use a different `crossorigin` value:
+
+```ts
+useScript({
+ src: 'https://example.com/script.js',
+ crossorigin: 'use-credentials', // Send cookies with request
+})
+```
+
+### Global Configuration
+
+Change defaults for all scripts:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ defaultScriptOptions: {
+ crossorigin: false,
+ referrerpolicy: false,
+ }
+ }
+})
+```
+
+## Crossorigin Values
+
+| Value | Cookies Sent | Error Details | Use Case |
+|-------|-------------|---------------|----------|
+| `anonymous` | No | Yes (if server supports) | Privacy-focused default |
+| `use-credentials` | Yes | Yes | Scripts requiring auth |
+| `false` | Yes | No | Scripts without CORS support |
+
+## Registry Scripts
+
+Many registry scripts already disable CORS attributes because the third-party doesn't support them:
+
+```ts
+const config = {
+ scriptInput: {
+ src: 'https://js.stripe.com/basil/stripe.js',
+ crossorigin: false,
+ referrerpolicy: false,
+ }
+}
+```
+
+Scripts with `crossorigin: false` include:
+- Stripe
+- YouTube Player
+- Google Sign-In
+- Google reCAPTCHA
+- Meta Pixel
+- TikTok Pixel
+- X (Twitter) Pixel
+- Snapchat Pixel
+- Cloudflare Web Analytics
+- Lemon Squeezy
+- Matomo Analytics
+
+If a registry script fails, check if CORS configuration needs adjustment.
+
+## Subresource Integrity
+
+When using [bundled scripts with SRI](/docs/guides/bundling#subresource-integrity-sri), you must include `crossorigin="anonymous"`, which Nuxt adds automatically:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ integrity: true, // Automatically sets crossorigin="anonymous"
+ }
+ }
+})
+```
+
+## Troubleshooting
+
+### Script Won't Load
+
+1. Check browser console for CORS errors
+2. Set `crossorigin: false` to disable CORS mode
+3. Verify the third-party server supports CORS headers
+
+### Script Loads but Broken
+
+1. The script may require cookies - try `crossorigin: 'use-credentials'`
+2. The script may need the referrer - set `referrerpolicy: false`
+3. Check if the script expects you to load it without CORS attributes
+
+### Debugging External Script Errors
+
+To see full error messages from external scripts:
+
+1. Ensure the script has `crossorigin="anonymous"`
+2. Verify the server returns `Access-Control-Allow-Origin` header
+3. If the server doesn't support CORS, you won't get detailed errors
+
+### Bundling as Alternative
+
+If CORS issues persist, consider [bundling the script](/docs/guides/bundling) to serve it from your own domain, eliminating CORS entirely.
diff --git a/docs/content/docs/1.guides/6.v1-migration.md b/docs/content/docs/1.guides/6.v1-migration.md
new file mode 100644
index 00000000..1229809c
--- /dev/null
+++ b/docs/content/docs/1.guides/6.v1-migration.md
@@ -0,0 +1,202 @@
+---
+
+title: v1 Migration Guide
+description: Migration guide for upgrading from v0.x to v1.0.
+
+---
+
+## 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
+
+Route all script traffic through your own domain for privacy and ad-blocker compatibility.
+
+```ts
+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](/docs/guides/first-party) for details.
+
+### Partytown Support
+
+Load scripts off the main thread using web workers.
+
+```ts
+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.
+
+::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
+GA4 has [known issues](https://github.com/BuilderIO/partytown/issues/583) with Partytown. GTM is not compatible. Consider Plausible, Fathom, or Umami instead.
+::
+
+### SSR Social Embeds
+
+Render X, Instagram, and Bluesky embeds server-side without loading third-party JavaScript.
+
+Enable the embeds you need in your `nuxt.config`:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ xEmbed: true,
+ instagramEmbed: true,
+ blueskyEmbed: true,
+ },
+ },
+})
+```
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- Zero third-party JavaScript
+- No cookies set by X/Instagram
+- User IPs not shared
+- All content served from your domain
+
+### Script Reload
+
+Re-execute DOM-scanning scripts after SPA navigation:
+
+```ts
+const script = useScript('/third-party.js')
+await script.reload()
+```
+
+### SRI Integrity Hashes
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ assets: {
+ integrity: 'sha384' // or 'sha256', 'sha512'
+ }
+ }
+})
+```
+
+### GTM Default Consent
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleTagManager: {
+ id: 'GTM-XXXX',
+ defaultConsent: {
+ ad_storage: 'denied',
+ analytics_storage: 'denied'
+ }
+ }
+ }
+ }
+})
+```
+
+### New Registry Scripts
+
+- **PostHog**: Product analytics with feature flags
+- **Google reCAPTCHA v3**: Invisible bot protection
+- **TikTok Pixel**: Conversion tracking
+- **Google Sign-In**: One-tap authentication
+
+### Google Maps Color Mode
+
+```vue
+
+```
+
+Auto-switches with `@nuxtjs/color-mode` or manual `color-mode` prop.
+
+## Breaking Changes
+
+### YouTube Player
+
+#### Aspect Ratio
+
+Use `ratio` prop instead of deriving from `width/height`:
+
+```diff
+
+```
+
+Default is `16/9`.
+
+#### Placeholder Image
+
+Default `object-fit` changed from `contain` to `cover`:
+
+```vue
+
+```
+
+#### 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:
+
+```ts
+let initialized = false
+useScriptGoogleTagManager({
+ onBeforeGtmStart: (gtag) => {
+ if (initialized)
+ return
+ initialized = true
+ // your init code
+ }
+})
+```
+
+### Type Augmentation
+
+Templates reorganized. Run `nuxi prepare` after upgrading.
diff --git a/docs/content/docs/3.api/_dir.yml b/docs/content/docs/3.api/.navigation.yml
similarity index 100%
rename from docs/content/docs/3.api/_dir.yml
rename to docs/content/docs/3.api/.navigation.yml
diff --git a/docs/content/docs/3.api/1.use-script.md b/docs/content/docs/3.api/1.use-script.md
index 3eb29008..76237405 100644
--- a/docs/content/docs/3.api/1.use-script.md
+++ b/docs/content/docs/3.api/1.use-script.md
@@ -1,14 +1,20 @@
---
-title: useScript
+
+title: useScript()
description: API documentation for the useScript function.
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/composables/useScript.ts
size: xs
+
---
-This composable is a wrapper around the Unhead [useScript](https://unhead.unjs.io/usage/composables/use-script) with extra Nuxt goodies on top, for
+::callout{icon="i-heroicons-play" to="https://stackblitz.com/github/nuxt/scripts/tree/main/examples/custom-script" target="_blank"}
+Try the live [Custom Script Example](https://stackblitz.com/github/nuxt/scripts/tree/main/examples/custom-script) on [StackBlitz](https://stackblitz.com).
+::
+
+This composable is a wrapper around the Unhead [`useScript()`{lang="ts"}](https://unhead.unjs.io/usage/composables/use-script){lang="ts"} with extra Nuxt goodies on top, for
full documentation please refer to this.
## Signature
@@ -67,6 +73,14 @@ export type NuxtUseScriptOptions = Omit, 'trigger'>
* - `false` - Do not bundle the script. (default)
*/
bundle?: boolean
+ /**
+ * [Experimental] Load the script in a web worker using Partytown.
+ * When enabled, adds `type="text/partytown"` to the script tag.
+ * Requires @nuxtjs/partytown to be installed and configured separately.
+ * Note: Scripts requiring DOM access (GTM, Hotjar, chat widgets) are not compatible.
+ * @see https://partytown.qwik.dev/
+ */
+ partytown?: boolean
/**
* Skip any schema validation for the script input. This is useful for loading the script stubs for development without
* loading the actual script and not getting warnings.
@@ -78,3 +92,25 @@ export type NuxtUseScriptOptions = Omit, 'trigger'>
## Return
See the [Understanding proxied functions](/docs/guides/key-concepts) and [$script](https://unhead.unjs.io/usage/composables/use-script#argument-use-script-options) documentation for more information on the return.
+
+The returned object includes:
+
+- `status` - Reactive ref with the script status: `'awaitingLoad'` | `'loading'` | `'loaded'` | `'error'`
+- `load()`{lang="ts"} - Function to manually load the script
+- `remove()`{lang="ts"} - Function to remove the script from the DOM
+- `reload()`{lang="ts"} - Function to remove and reload the script (see below)
+
+### `reload()`{lang="ts"}
+
+Removes the script and reloads it, forcing re-execution. Useful for third-party scripts that scan the DOM once on load and need to re-run after SPA navigation.
+
+```ts
+const { reload } = useScript('https://example.com/dom-scanner.js')
+
+// Reload when navigating
+watch(() => route.path, () => reload())
+```
+
+::callout{icon="i-heroicons-light-bulb" color="blue"}
+Many third-party scripts have their own SPA support (e.g., `_iub.cs.api.activateSnippets()`{lang="ts"} for iubenda). Check the script's documentation before using `reload()`{lang="ts"} - their built-in methods are usually more efficient.
+::
diff --git a/docs/content/docs/3.api/3.use-script-trigger-consent.md b/docs/content/docs/3.api/3.use-script-trigger-consent.md
index 1553c2b0..92f47e9c 100644
--- a/docs/content/docs/3.api/3.use-script-trigger-consent.md
+++ b/docs/content/docs/3.api/3.use-script-trigger-consent.md
@@ -1,14 +1,20 @@
---
-title: useScriptTriggerConsent
+
+title: useScriptTriggerConsent()
description: API documentation for the useScriptTriggerConsent function.
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/composables/useScriptTriggerConsent.ts
size: xs
+
---
-Load a script once consent has been provided either through a resolvable `consent` or calling the `accept` method.
+Load a script once you provide consent through either a resolvable `consent` or the `accept` method.
+
+::callout
+The trigger by itself does nothing - scripts will only load when consent is granted through either the `accept()`{lang="ts"} method or by providing a `consent` option that resolves to `true`.
+::
## Signature
@@ -35,7 +41,7 @@ export interface ConsentScriptTriggerOptions {
## Returns
-A extended promise api with an `accept` method to accept the consent and load the script.
+A extended promise API with an `accept` method to accept the consent and load the script.
```ts
interface UseConsentScriptTriggerApi extends Promise {
@@ -46,7 +52,9 @@ interface UseConsentScriptTriggerApi extends Promise {
}
```
-## Example
+## Examples
+
+### Basic Usage
```vue [app.vue]
+
+
+
+ Click me to load the script
+
+
+```
+
## Examples
diff --git a/docs/content/docs/3.api/3.use-script-trigger-idle-timeout.md b/docs/content/docs/3.api/3.use-script-trigger-idle-timeout.md
new file mode 100644
index 00000000..90af193d
--- /dev/null
+++ b/docs/content/docs/3.api/3.use-script-trigger-idle-timeout.md
@@ -0,0 +1,134 @@
+---
+
+title: useScriptTriggerIdleTimeout()
+description: API documentation for the useScriptTriggerIdleTimeout function.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/composables/useScriptTriggerIdleTimeout.ts
+ size: xs
+
+---
+
+Create a trigger that loads a script after an idle timeout once Nuxt is ready. This is useful for non-critical scripts that should load with a delay to further minimize impact on initial page performance.
+
+## Signature
+
+```ts
+function useScriptTriggerIdleTimeout(options: IdleTimeoutScriptTriggerOptions): Promise
+```
+
+## Arguments
+
+```ts
+export interface IdleTimeoutScriptTriggerOptions {
+ /**
+ * The timeout in milliseconds to wait before loading the script.
+ */
+ timeout: number
+}
+```
+
+## Returns
+
+A promise that resolves to `true` when the timeout completes and the script should load, or `false` if the trigger is cancelled.
+
+## Nuxt Config Usage
+
+For convenience, you can use this trigger directly in your `nuxt.config` without calling the composable explicitly:
+
+::code-group
+
+```ts [Registry Script]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleAnalytics: [{
+ id: 'GA_MEASUREMENT_ID'
+ }, {
+ trigger: { idleTimeout: 3000 } // Load 3 seconds after Nuxt ready
+ }]
+ }
+ }
+})
+```
+
+```ts [Global Script]
+export default defineNuxtConfig({
+ scripts: {
+ globals: {
+ chatWidget: ['https://widget.example.com/chat.js', {
+ trigger: { idleTimeout: 5000 } // Load 5 seconds after Nuxt ready
+ }]
+ }
+ }
+})
+```
+
+::
+
+## Examples
+
+### Basic Usage
+
+Load a script 5 seconds after Nuxt is ready:
+
+```ts
+const script = useScript({
+ src: 'https://example.com/analytics.js',
+}, {
+ trigger: useScriptTriggerIdleTimeout({ timeout: 5000 })
+})
+```
+
+### Delayed Analytics Loading
+
+Perfect for analytics scripts that don't need to load immediately:
+
+```vue
+
+```
+
+### Progressive Enhancement
+
+Load enhancement scripts with a delay to prioritize critical resources:
+
+```vue
+
+```
+
+## Best Practices
+
+- **Use appropriate timeouts**: 3-5 seconds for analytics, 5-10 seconds for widgets, longer for non-essential features
+- **Consider user behavior**: Shorter timeouts for high-engagement pages, longer for content-focused pages
+- **Monitor performance**: Ensure delayed loading improves your Core Web Vitals
+- **Combine with other triggers**: Use alongside interaction triggers for optimal user experience
diff --git a/docs/content/docs/3.api/3.use-script-trigger-interaction.md b/docs/content/docs/3.api/3.use-script-trigger-interaction.md
new file mode 100644
index 00000000..26fe7095
--- /dev/null
+++ b/docs/content/docs/3.api/3.use-script-trigger-interaction.md
@@ -0,0 +1,203 @@
+---
+
+title: useScriptTriggerInteraction()
+description: API documentation for the useScriptTriggerInteraction function.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/composables/useScriptTriggerInteraction.ts
+ size: xs
+
+---
+
+Create a trigger that loads a script when any of the specified user interaction events occur. This is perfect for loading scripts only when users interact with your site, providing excellent performance optimization.
+
+## Signature
+
+```ts
+function useScriptTriggerInteraction(options: InteractionScriptTriggerOptions): Promise
+```
+
+## Arguments
+
+```ts
+export interface InteractionScriptTriggerOptions {
+ /**
+ * The interaction events to listen for.
+ */
+ events: string[]
+ /**
+ * The element to listen for events on.
+ * @default document.documentElement
+ */
+ target?: EventTarget | null
+}
+```
+
+## Returns
+
+A promise that resolves to `true` when any of the specified interaction events occur and the script should load, or `false` if the trigger is cancelled.
+
+## Nuxt Config Usage
+
+For convenience, you can use this trigger directly in your `nuxt.config` without calling the composable explicitly:
+
+::code-group
+
+```ts [Registry Script]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleAnalytics: [{
+ id: 'GA_MEASUREMENT_ID'
+ }, {
+ trigger: { interaction: ['scroll', 'click', 'keydown'] }
+ }]
+ }
+ }
+})
+```
+
+```ts [Global Script]
+export default defineNuxtConfig({
+ scripts: {
+ globals: {
+ chatWidget: ['https://widget.example.com/chat.js', {
+ trigger: { interaction: ['scroll', 'click', 'touchstart'] }
+ }]
+ }
+ }
+})
+```
+
+::
+
+## Common Event Types
+
+Here are some commonly used interaction events:
+
+- **Mouse events**: `click`, `mousedown`, `mouseup`, `mouseover`, `mouseenter`, `mouseleave`
+- **Touch events**: `touchstart`, `touchend`, `touchmove`
+- **Keyboard events**: `keydown`, `keyup`, `keypress`
+- **Scroll events**: `scroll`, `wheel`
+- **Focus events**: `focus`, `blur`, `focusin`, `focusout`
+
+## Examples
+
+### Basic Usage
+
+Load a script when the user scrolls, clicks, or presses a key:
+
+```ts
+const script = useScript({
+ src: 'https://example.com/chat-widget.js',
+}, {
+ trigger: useScriptTriggerInteraction({
+ events: ['scroll', 'click', 'keydown']
+ })
+})
+```
+
+### Analytics on First Interaction
+
+Load analytics only when users interact with your site:
+
+```vue
+
+```
+
+### Specific Element Targeting
+
+Listen for events on a specific element:
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+### Progressive Feature Loading
+
+Load features based on different interaction types:
+
+```vue
+
+```
+
+### Mobile-Optimized Loading
+
+Use touch events for mobile-specific optimizations:
+
+```vue
+
+```
+
+## Best Practices
+
+- **Start with common events**: Use `['scroll', 'click', 'keydown']` as a good default for most use cases
+- **Consider mobile users**: Include touch events like `touchstart` for mobile optimization
+- **Target specific elements**: Use the `target` option to listen only on relevant parts of your page
+- **Combine events wisely**: Don't overload with too many event types - focus on meaningful interactions
+- **Test performance impact**: Verify that interaction-based loading improves your metrics
+- **Fallback considerations**: Consider what happens if users don't interact (e.g., combine with a timeout trigger)
diff --git a/docs/content/docs/3.api/4.use-script-event-page.md b/docs/content/docs/3.api/4.use-script-event-page.md
index d63cffdf..685f1777 100644
--- a/docs/content/docs/3.api/4.use-script-event-page.md
+++ b/docs/content/docs/3.api/4.use-script-event-page.md
@@ -1,11 +1,13 @@
---
-title: useScriptEventPage
+
+title: useScriptEventPage()
description: API documentation for the useScriptEventPage function.
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/composables/useScriptEventPage.ts
size: xs
+
---
Access the current page title and path and trigger an event when they change.
@@ -18,7 +20,7 @@ function useScriptEventPage(callback?: (title: string, path: string) => void): R
#### Arguments
-- `callback` (optional) - A function that will be called when the page title or path changes.
+- `callback` (optional) - A function that Nuxt calls when the page title or path changes.
#### Returns
diff --git a/docs/content/docs/3.api/5.nuxt-config.md b/docs/content/docs/3.api/5.nuxt-config.md
index 206f198d..df387434 100644
--- a/docs/content/docs/3.api/5.nuxt-config.md
+++ b/docs/content/docs/3.api/5.nuxt-config.md
@@ -1,51 +1,131 @@
---
+
title: Nuxt Config
description: Configure Nuxt Scripts using your Nuxt Config.
+
---
-## `registry`
+## `registry`{lang="ts"}
-- Type: `ScriptRegistry`
+- Type: `ScriptRegistry`{lang="ts"}
-Global registry scripts that should be loaded.
+Global registry scripts to load.
See the [Script Registry](/scripts) for more details.
-## `defaultScriptOptions`
+## `partytown`{lang="ts"} :badge[Experimental]{color="amber"}
+
+- Type: `(keyof ScriptRegistry)[]`{lang="ts"}
+- Default: `[]`
-- Type: `NuxtUseScriptOptions`
+Registry scripts to load via [Partytown](https://partytown.qwik.dev/) (web worker).
+
+Listing a script here loads it with `type="text/partytown"` so Partytown can execute it in a web worker.
+
+::code-group
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ partytown: ['googleAnalytics', 'plausible', 'fathom'],
+ registry: {
+ googleAnalytics: { id: 'G-XXXXX' },
+ plausible: { domain: 'example.com' },
+ fathom: { site: 'XXXXX' }
+ }
+ }
+})
+```
+::
+
+::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
+Requires you to install [`@nuxtjs/partytown`](https://github.com/nuxt-modules/partytown). Nuxt automatically configures the `forward` array for supported registry scripts.
+::
+
+### Supported Scripts
+
+Scripts with auto-forwarding configured:
+- `googleAnalytics`, `plausible`, `fathom`, `umami`, `matomo`, `segment`
+- `metaPixel`, `xPixel`, `tiktokPixel`, `snapchatPixel`, `redditPixel`
+- `cloudflareWebAnalytics`
+
+### Limitations
+
+::callout{icon="i-heroicons-x-circle" color="red"}
+**Incompatible scripts** - The following cannot work with Partytown due to DOM access requirements:
+- **Tag managers**: Google Tag Manager, Adobe Launch
+- **Session replay**: Hotjar, Clarity, FullStory, LogRocket
+- **Chat widgets**: Intercom, Crisp, Drift, HubSpot Chat
+- **Video players**: [YouTube](https://youtube.com), Vimeo embeds
+- **A/B testing**: Optimizely, VWO, Google Optimize
+::
+
+::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
+**General limitations**:
+- Scripts cannot directly access or modify the DOM
+- `document.cookie` access requires additional Partytown configuration
+- Debugging is more complex (code runs in a web worker)
+- Some scripts may have timing differences compared to main thread execution
+- localStorage/sessionStorage access may require forwarding configuration
+::
+
+## `defaultScriptOptions`{lang="ts"}
+
+- Type: `NuxtUseScriptOptions`{lang="ts"}
- Default: `{ trigger: 'onNuxtReady' }`
-Default options for scripts. See the [useScript](/docs/api/use-script) documentation for more details.
+Default options for scripts. See the [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} documentation for more details.
-## `globals`
+## `globals`{lang="ts"}
-- Type: `(NuxtUseScriptInput | [NuxtUseScriptInput, NuxtUseScriptOptions])[]`
+- Type: `(NuxtUseScriptInput | [NuxtUseScriptInput, NuxtUseScriptOptions])[]`{lang="ts"}
- Default: `[]`
-Global scripts that should be loaded on all pages. This is a configuration for the [useScript](/docs/api/use-script) composable.
+Global scripts to load on all pages. This configuration applies to the [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} composable.
See the [Globals](/docs/guides/global) documentation for more details.
-## `assets`
+## `enabled`{lang="ts"}
+
+- Type: `boolean`{lang="ts"}
+- Default: `true`
+
+Disables the Nuxt Scripts module.
+
+## `debug`{lang="ts"}
+
+- Type: `boolean`{lang="ts"}
+- Default: `false`
+
+Enable to see debug logs.
+
+## `assets`{lang="ts"}
-- Type: `object`
+- Type: `object`{lang="ts"}
- Default: `{ prefix: '/_scripts/', strategy: 'public' }`
-Controls the way scripts are bundled to be served by Nuxt.
+Controls how Nuxt bundles scripts for serving.
See the [Bundling](/docs/guides/bundling) documentation for more details.
-## `enabled`
+## `assets.fallbackOnSrcOnBundleFail`{lang="ts"}
-- Type: `boolean`
-- Default: `true`
+- Type: `boolean`{lang="ts"}
+- Default: `false`
-Disables the Nuxt Scripts module.
+Fallback to the remote src URL when `bundle` fails when enabled. By default, the bundling process stops if the third-party script can't be downloaded.
-## `debug`
+## `assets.fetchOptions`{lang="ts"}
-- Type: `boolean`
+- Type: `object`{lang="ts"}
+- Default: `{ retry: 3, retryDelay: 2000, timeout: 15_000 }`
+
+Options to pass to the fetch function when downloading scripts.
+
+## `assets.integrity`{lang="ts"}
+
+- Type: `boolean | 'sha256' | 'sha384' | 'sha512'`{lang="ts"}
- Default: `false`
-Enable to see debug logs.
+Enable automatic Subresource Integrity (SRI) hash generation for bundled scripts. When enabled, calculates a cryptographic hash of each bundled script and injects the `integrity` attribute along with `crossorigin="anonymous"`.
+
+See the [Bundling - Subresource Integrity](/docs/guides/bundling#subresource-integrity-sri) documentation for more details.
diff --git a/docs/content/docs/3.api/6.nuxt-app-hooks.md b/docs/content/docs/3.api/6.nuxt-app-hooks.md
index f878e40c..701eb1bf 100644
--- a/docs/content/docs/3.api/6.nuxt-app-hooks.md
+++ b/docs/content/docs/3.api/6.nuxt-app-hooks.md
@@ -1,15 +1,17 @@
---
+
title: Nuxt App Hooks
description: Use Nuxt App hooks to extend the Nuxt Scripts runtime behavior.
+
---
-## `scripts:updated`
+## `scripts:updated`{lang="ts"}
-- Type: `async (ctx: { scripts: ScriptRegistry }) => HookResult`
+- Type: `async (ctx: { scripts: ScriptRegistry }) => HookResult`{lang="ts"}
-Triggered after the script status is updated.
+Triggered after Nuxt updates the script status.
-This is used internally for the DevTools but can be used however you see fit.
+Nuxt uses this internally for the DevTools, but you can use it however you see fit.
```ts [plugins/nuxt-scripts.ts]
export default defineNuxtPlugin({
@@ -21,13 +23,13 @@ export default defineNuxtPlugin({
})
```
-## `script:instance-fn`
+## `script:instance-fn`{lang="ts"}
-- Type: `(ctx: { script: ScriptInstance, fn: string | symbol, args: any, exists: boolean }) => HookResult`
+- Type: `(ctx: { script: ScriptInstance, fn: string | symbol, args: any, exists: boolean }) => HookResult`{lang="ts"}
This is exposed only from Unhead, it's fired when accessing properties via the proxy instance.
-This is also used internally for the DevTools but can be used however you see fit.
+Nuxt also uses this internally for the DevTools, but you can use it however you see fit.
```ts
export default defineNuxtPlugin({
diff --git a/docs/content/docs/3.api/6.nuxt-hooks.md b/docs/content/docs/3.api/6.nuxt-hooks.md
index bc312cc2..58ef151b 100644
--- a/docs/content/docs/3.api/6.nuxt-hooks.md
+++ b/docs/content/docs/3.api/6.nuxt-hooks.md
@@ -1,21 +1,23 @@
---
+
title: Nuxt Hooks
description: Use Nuxt hooks to extend the Nuxt Scripts module.
+
---
-## `scripts:registry`
+## `scripts:registry`{lang="ts"}
-- Type: `async (ctx: { registry: ScriptRegistry }) => HookResult`
+- Type: `async (registry: RegistryScripts) => HookResult`{lang="ts"}
-Add registry scripts at build, allowing them to be loaded via `scripts.registry` and bundled if available.
+Add registry scripts at build, allowing you to load them via `scripts.registry` and bundle them if available.
-This is intended to be used by modules.
+Modules should use this hook.
```ts [module.ts]
export default defineNuxtModule({
setup() {
- nuxt.hooks.hook('scripts:registry', async (ctx) => {
- ctx.registry.add({
+ nuxt.hooks.hook('scripts:registry', async (registry) => {
+ registry.push({
// used in DevTools
label: 'My Custom Script',
logo: ``,
diff --git a/docs/content/scripts/.navigation.yml b/docs/content/scripts/.navigation.yml
new file mode 100644
index 00000000..fc997444
--- /dev/null
+++ b/docs/content/scripts/.navigation.yml
@@ -0,0 +1 @@
+title: Script Registry
diff --git a/docs/content/scripts/_dir.yml b/docs/content/scripts/_dir.yml
deleted file mode 100644
index 94164c7e..00000000
--- a/docs/content/scripts/_dir.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-icon: i-ph-floppy-disk-duotone
-title: Script Registry
diff --git a/docs/content/scripts/ads/google-adsense.md b/docs/content/scripts/ads/google-adsense.md
deleted file mode 100644
index f083e567..00000000
--- a/docs/content/scripts/ads/google-adsense.md
+++ /dev/null
@@ -1,92 +0,0 @@
----
-title: Google Adsense
-description: Show Google Adsense ads in your Nuxt app.
-links:
- - label: useScriptGoogleAdsense
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-adsense.ts
- size: xs
- - label: ""
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptGoogleAdsense.vue
- size: xs
----
-
-:UAlert{title="Experimental" description="The Google Adsense integration has not been fully tested, use with caution." color="yellow" variant="soft" class="not-prose"}
-
-[Google Adsense](https://www.google.com/adsense/start/) allows you to monetize your website by displaying ads.
-
-Nuxt Scripts provides a `useScriptGoogleAdsense` composable and a headless `ScriptGoogleAdsense` component to interact with the Google Adsense.
-
-## ScriptGoogleAdsense
-
-The `ScriptGoogleAdsense` component is a wrapper around the `useScriptGoogleAdsense` composable. It provides a simple way to embed ads in your Nuxt app.
-
-```vue
-
-
-
-```
-
-### Handling Ad-blockers
-
-You can use these hooks to add a fallback when the Google Adsense script is blocked.
-
-```vue
-
-
-
-
- Please support us by disabling your ad blocker.
-
-
-
-```
-
-
-### Component API
-
-See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-
-### Props
-
-The `ScriptGoogleAdsense` component supports all props that Google Adsense supports on the `` tag. See the [Ad tags documentation](https://developers.google.com/adsense/platforms/transparent/ad-tags) for more information.
-
-At a minimum you must provide the following tags:
-- `data-ad-client`: The Google Adsense ID.
-- `data-ad-slot`: The slot ID.
-
-## useScriptGoogleAdsense
-
-The `useScriptGoogleAdsense` composable lets you have fine-grain control over the Google Adsense script.
-
-```ts
-export function useScriptGoogleAdsense(_options?: GoogleAdsenseInput) {}
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### GoogleAdsenseApi
-
-```ts
-export interface GoogleAdsenseApi {
- adsbygoogle: any[] & { loaded: boolean }
-}
-```
-
-### GoogleAdsenseInput
-
-```ts
-export const GoogleAdsenseOptions = object({
- /**
- * The Google Adsense ID.
- */
- client: optional(string()),
-})
-```
diff --git a/docs/content/scripts/analytics/cloudflare-web-analytics.md b/docs/content/scripts/analytics/cloudflare-web-analytics.md
deleted file mode 100644
index c63625d0..00000000
--- a/docs/content/scripts/analytics/cloudflare-web-analytics.md
+++ /dev/null
@@ -1,138 +0,0 @@
----
-title: Cloudflare Web Analytics
-description: Use Cloudflare Web Analytics in your Nuxt app.
-links:
- - label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/cloudflare-web-analytics.ts
- size: xs
----
-
-[Cloudflare Web Analytics](https://developers.cloudflare.com/analytics/web-analytics/) with Nuxt is a great privacy analytics solution. It offers free, privacy-centric analytics for your website. It doesn't gather personal data from your visitors, yet provides detailed insights into your web pages' performance as experienced by your visitors.
-
-The simplest way to load Cloudflare Web Analytics globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptCloudflareWebAnalytics](#usescriptcloudflarewebanalytics) composable.
-
-## Loading Globally
-
-If you'd like to avoid loading the analytics in development, you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) in your Nuxt config.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- cloudflareWebAnalytics: {
- token: 'YOUR_TOKEN_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- cloudflareWebAnalytics: {
- token: 'YOUR_TOKEN_ID',
- }
- }
- }
- }
-})
-```
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- cloudflareWebAnalytics: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- cloudflareWebAnalytics: {
- // .env
- // NUXT_PUBLIC_SCRIPTS_CLOUDFLARE_WEB_ANALYTICS_TOKEN=
- token: '',
- },
- },
- },
- },
-})
-```
-
-::
-
-## useScriptCloudflareWebAnalytics
-
-The `useScriptCloudflareWebAnalytics` composable lets you have fine-grain control over when and how Cloudflare Web Analytics is loaded on your site.
-
-```ts
-function useScriptCloudflareWebAnalytics(_options?: CloudflareWebAnalyticsInput) {}
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-The composable comes with the following defaults:
-- **Trigger: Client** Script will load when the Nuxt is hydrating to keep web vital metrics accurate.
-
-### CloudflareWebAnalyticsInput
-
-```ts
-export const CloudflareWebAnalyticsOptions = object({
- /**
- * The Cloudflare Web Analytics token.
- */
- token: string([minLength(32)]),
- /**
- * Cloudflare Web Analytics enables measuring SPAs automatically by overriding the History API’s pushState function
- * and listening to the onpopstate. Hash-based router is not supported.
- *
- * @default true
- */
- spa: optional(boolean()),
-})
-```
-
-### CloudflareWebAnalyticsApi
-
-```ts
-export interface CloudflareWebAnalyticsApi {
- __cfBeacon: {
- load: 'single'
- spa: boolean
- token: string
- }
-}
-```
-
-## Example
-
-Loading Cloudflare Web Analytics through the `app.vue` when Nuxt is ready.
-
-```vue [app.vue]
-
-```
-
-The Cloudflare Web Analytics composable injects a `window.__cfBeacon` object into the global scope. If you need
-to access this you can do by awaiting the script.
-
-```ts
-const { onLoaded } = useScriptCloudflareWebAnalytics()
-onLoaded(({ cfBeacon }) => {
- console.log(cfBeacon)
-})
-```
diff --git a/docs/content/scripts/analytics/fathom-analytics.md b/docs/content/scripts/analytics/fathom-analytics.md
deleted file mode 100644
index ee7924d2..00000000
--- a/docs/content/scripts/analytics/fathom-analytics.md
+++ /dev/null
@@ -1,168 +0,0 @@
----
-title: Fathom Analytics
-description: Use Fathom Analytics in your Nuxt app.
-links:
- - label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/fathom-analytics.ts
- size: xs
----
-
-[Fathom Analytics](https://usefathom.com/) is a great privacy analytics solution for your Nuxt app. It doesn't gather personal data from your visitors, yet provides detailed insights into how your site is used.
-
-## Loading Globally
-
-The simplest way to load Fathom Analytics globally in your Nuxt App is to use your Nuxt config, providing your site ID
-as a string.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- fathomAnalytics: {
- site: 'YOUR_TOKEN_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- fathomAnalytics: {
- site: 'YOUR_SITE_ID'
- }
- }
- }
- },
-})
-```
-
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- fathomAnalytics: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- fathomAnalytics: {
- // .env
- // NUXT_PUBLIC_SCRIPTS_FATHOM_ANALYTICS_SITE=
- token: '',
- },
- },
- },
- },
-})
-```
-
-::
-
-## Composable `useScriptFathomAnalytics`
-
-The `useScriptFathomAnalytics` composable lets you have fine-grain control over when and how Fathom Analytics is loaded on your site.
-
-```ts
-useScriptFathomAnalytics(options)
-```
-
-## Defaults
-
-- **Trigger**: Script will load when Nuxt is hydrated.
-
-## Options
-
-```ts
-export const FathomAnalyticsOptions = object({
- /**
- * The Fathom Analytics site ID.
- */
- site: string(),
- /**
- * The Fathom Analytics tracking mode.
- */
- spa: optional(union([literal('auto'), literal('history'), literal('hash')])),
- /**
- * Automatically track page views.
- */
- auto: optional(boolean()),
- /**
- * Enable canonical URL tracking.
- */
- canonical: optional(boolean()),
- /**
- * Honor Do Not Track requests.
- */
- honorDnt: optional(boolean()),
-})
-```
-
-Additionally like all registry scripts you can provide extra configuration:
-
-- `scriptInput` - HTML attributes to add to the script tag.
-- `scriptOptions` - See [Script Options]. Bundling is not supported, `bundle: true` will not do anything.
-
-## Return values
-
-The Fathom Analytics composable injects a `window.fathom` object into the global scope.
-
-```ts
-export interface FathomAnalyticsApi {
- beacon: (ctx: { url: string, referrer?: string }) => void
- blockTrackingForMe: () => void
- enableTrackingForMe: () => void
- isTrackingEnabled: () => boolean
- send: (type: string, data: unknown) => void
- setSite: (siteId: string) => void
- sideId: string
- trackPageview: (ctx?: { url: string, referrer?: string }) => void
- trackGoal: (goalId: string, cents: number) => void
- trackEvent: (eventName: string, value: { _value: number }) => void
-}
-```
-
-You can access the `fathom` object as a proxy directly or await the `$script` promise to access the object. It's recommended
-to use the proxy for any void functions.
-
-::code-group
-
-```ts [Proxy]
-const { proxy } = useScriptFathomAnalytics()
-function trackMyGoal() {
- proxy.trackGoal('MY_GOAL_ID', 100)
-}
-```
-
-```ts [onLoaded]
-const { onLoaded } = useScriptFathomAnalytics()
-onLoaded(({ trackGoal }) => {
- trackGoal('MY_GOAL_ID', 100)
-})
-```
-
-::
-
-## Example
-
-Loading Fathom Analytics through the `app.vue` when Nuxt is ready.
-
-```vue [app.vue]
-
-```
diff --git a/docs/content/scripts/analytics/google-analytics.md b/docs/content/scripts/analytics/google-analytics.md
deleted file mode 100644
index 0096c5cc..00000000
--- a/docs/content/scripts/analytics/google-analytics.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-title: Google Analytics
-description: Use Google Analytics in your Nuxt app.
-links:
- - label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-analytics.ts
- size: xs
----
-
-::tip
-This composable is generated with [GoogleChromeLabs/third-party-capital](https://github.com/GoogleChromeLabs/third-party-capital) in collaboration with the [Chrome Aurora team](https://developer.chrome.com/docs/aurora).
-::
-
-[Google Analytics](https://marketingplatform.google.com/about/analytics/) is an analytics solution for Nuxt Apps.
-
-It provides detailed insights into how your website is performing, how users are interacting with your content, and how they are navigating through your site.
-
-The simplest way to load Google Analytics globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptGoogleAnalytics](#useScriptGoogleAnalytics) composable.
-
-### Loading Globally
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- googleAnalytics: {
- id: 'YOUR_ID',
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- googleAnalytics: {
- id: 'YOUR_ID',
- }
- }
- }
- }
-})
-```
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- googleAnalytics: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- googleAnalytics: {
- // .env
- // NUXT_PUBLIC_SCRIPTS_GOOGLE_ANALYTICS_ID=
- id: '',
- },
- },
- },
- },
-})
-```
-
-::
-
-## useScriptGoogleAnalytics
-
-The `useScriptGoogleAnalytics` composable lets you have fine-grain control over when and how Google Analytics is loaded on your site.
-
-```ts
-const googleAnalytics = useScriptGoogleAnalytics({
- id: 'YOUR_ID'
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### GoogleAnalyticsApi
-
-```ts
-interface GTag {
- (fn: 'js', opt: Date): void
- (fn: 'config', opt: string): void
- (fn: 'event', opt: string, opt2?: {
- [key: string]: any
- }): void
- (fn: 'set', opt: {
- [key: string]: string
- }): void
- (fn: 'get', opt: string): void
- (fn: 'consent', opt: 'default', opt2: {
- [key: string]: string
- }): void
- (fn: 'consent', opt: 'update', opt2: {
- [key: string]: string
- }): void
- (fn: 'config', opt: 'reset'): void
-}
-interface GoogleAnalyticsApi {
- dataLayer: Record[]
- gtag: GTag
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const GoogleAnalyticsOptions = object({
- /**
- * The Google Analytics ID.
- */
- id: string(),
- /**
- * The datalayer's name you want it to be associated with
- */
- dataLayerName: optional(string())
-})
-```
-
-## Example
-
-Using Google Analytics only in production while using `gtag` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-::
diff --git a/docs/content/scripts/analytics/matomo-analytics.md b/docs/content/scripts/analytics/matomo-analytics.md
deleted file mode 100644
index 8563e61a..00000000
--- a/docs/content/scripts/analytics/matomo-analytics.md
+++ /dev/null
@@ -1,136 +0,0 @@
----
-title: Matomo Analytics
-description: Use Matomo Analytics in your Nuxt app.
-links:
- - label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/matomo-analytics.ts
- size: xs
----
-
-[Matomo Analytics](https://matomo.org/) is a great analytics solution for Nuxt Apps.
-
-It provides detailed insights into how your website is performing, how users are interacting with your content, and how they are navigating through your site.
-
-The simplest way to load Matomo Analytics globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptMatomoAnalytics](#useScriptMatomoAnalytics) composable.
-
-## Loading Globally
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- matomoAnalytics: {
- siteId: 'YOUR_SITE_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- matomoAnalytics: {
- siteId: 'YOUR_SITE_ID',
- }
- }
- }
- }
-})
-```
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- matomoAnalytics: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- matomoAnalytics: {
- // .env
- // NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_SITE_ID=
- siteId: '', // NUXT_PUBLIC_SCRIPTS_MATOMO_ANALYTICS_SITE_ID
- },
- },
- },
- },
-})
-```
-
-::
-
-## useScriptMatomoAnalytics
-
-The `useScriptMatomoAnalytics` composable lets you have fine-grain control over when and how Matomo Analytics is loaded on your site.
-
-```ts
-const matomoAnalytics = useScriptMatomoAnalytics({
- matomoUrl: 'YOUR_MATOMO_URL',
- siteId: 'YOUR_SITE_ID'
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### MatomoAnalyticsApi
-
-```ts
-interface MatomoAnalyticsApi {
- _paq: unknown[]
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-// matomoUrl and site are required
-export const MatomoAnalyticsOptions = object({
- matomoUrl: string(),
- siteId: string(),
- trackPageView: optional(boolean()),
- enableLinkTracking: optional(boolean()),
-})
-```
-
-## Example
-
-Using Matomo Analytics only in production while using `_paq` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-::
diff --git a/docs/content/scripts/analytics/plausible-analytics.md b/docs/content/scripts/analytics/plausible-analytics.md
deleted file mode 100644
index 60edf17c..00000000
--- a/docs/content/scripts/analytics/plausible-analytics.md
+++ /dev/null
@@ -1,148 +0,0 @@
----
-title: Plausible Analytics
-description: Use Plausible Analytics in your Nuxt app.
-links:
- - label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/plausible-analytics.ts
- size: xs
----
-
-[Plausible Analytics](https://plausible.io/) is a privacy-friendly analytics solution for Nuxt Apps, allowing you to track your website's traffic without compromising your users' privacy.
-
-The simplest way to load Plausible Analytics globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptPlausibleAnalytics](#useScriptPlausibleAnalytics) composable.
-
-## Loading Globally
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- plausibleAnalytics: {
- domain: 'YOUR_DOMAIN'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- plausibleAnalytics: {
- domain: 'YOUR_DOMAIN',
- }
- }
- }
- }
-})
-```
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- plausibleAnalytics: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- plausibleAnalytics: {
- // .env
- // NUXT_PUBLIC_SCRIPTS_PLAUSIBLE_ANALYTICS_DOMAIN=
- domain: ''
- },
- },
- },
- },
-})
-```
-
-::
-
-## useScriptPlausibleAnalytics
-
-The `useScriptPlausibleAnalytics` composable lets you have fine-grain control over when and how Plausible Analytics is loaded on your site.
-
-```ts
-const plausible = useScriptPlausibleAnalytics({
- domain: 'YOUR_DOMAIN'
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### Self-hosted Plausible
-
-If you are using a self-hosted version of Plausible, you will need to provide an explicit src for the script so that
-the API events are sent to the correct endpoint.
-
-```ts
-useScriptPlausible({
- scriptInput: {
- src: 'https://my-self-hosted-plausible.io/js/script.js'
- }
-})
-```
-
-### PlausibleAnalyticsApi
-
-```ts
-export interface PlausibleAnalyticsApi {
- plausible: ((event: '404', options: Record) => void) &
- ((event: 'event', options: Record) => void) &
- ((...params: any[]) => void) & {
- q: any[]
- }
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const PlausibleAnalyticsOptions = object({
- domain: string(), // required
- extension: optional(union([union(extensions), array(union(extensions))])),
-})
-```
-
-## Example
-
-Using Plausible Analytics only in production while using `plausible` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-::
diff --git a/docs/content/scripts/bluesky-embed.md b/docs/content/scripts/bluesky-embed.md
new file mode 100644
index 00000000..50b8a796
--- /dev/null
+++ b/docs/content/scripts/bluesky-embed.md
@@ -0,0 +1,287 @@
+---
+
+title: Bluesky Embed
+description: Server-side rendered Bluesky embeds with zero client-side API calls.
+links:
+ - label: ScriptBlueskyEmbed
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptBlueskyEmbed.vue
+ size: xs
+
+---
+
+[Bluesky](https://bsky.app) is a decentralized social media platform built on the AT Protocol.
+
+Nuxt Scripts provides a [``{lang="html"}](/scripts/bluesky-embed){lang="html"} component that fetches post data server-side and exposes it via slots for complete styling control. All data is proxied through your server - no client-side API calls to Bluesky.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Setup
+
+To use the Bluesky embed component, you must enable it in your `nuxt.config`:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ blueskyEmbed: true,
+ },
+ },
+})
+```
+
+This registers the required server API routes (`/_scripts/embed/bluesky` and `/_scripts/embed/bluesky-image`) that handle fetching post data and proxying images.
+
+## [``{lang="html"}](/scripts/bluesky-embed){lang="html"}
+
+The [``{lang="html"}](/scripts/bluesky-embed){lang="html"} component is a headless component that:
+- Fetches post data server-side via the Bluesky public API (AT Protocol)
+- Proxies all images through your server for privacy
+- Converts rich text facets (links, mentions, hashtags) to HTML
+- Exposes post data via scoped slots for custom rendering
+- Caches responses for 10 minutes
+- Respects author opt-out (`!no-unauthenticated` label)
+
+### Demo
+
+::code-group
+
+```vue [Basic Usage]
+
+
+
+
+
+
+
+```
+
+::
+
+### Slot Props
+
+The default slot receives the following props:
+
+```ts
+interface SlotProps {
+ // Raw data
+ post: BlueskyEmbedPostData
+ // Author info
+ displayName: string
+ handle: string
+ avatar: string // Proxied URL
+ avatarOriginal: string // Original Bluesky CDN URL
+ isVerified: boolean
+ // Post content
+ text: string // Plain text
+ richText: string // HTML with links, mentions, and hashtags
+ langs?: string[] // Language codes
+ // Formatted values
+ datetime: string // "12:47 PM · Feb 5, 2024"
+ createdAt: Date
+ likes: number
+ likesFormatted: string // "1.2K"
+ reposts: number
+ repostsFormatted: string // "234"
+ replies: number
+ repliesFormatted: string // "42"
+ quotes: number
+ quotesFormatted: string // "12"
+ // Media
+ images?: Array<{
+ thumb: string // Proxied thumbnail URL
+ fullsize: string // Proxied full-size URL
+ alt: string
+ aspectRatio?: { width: number, height: number }
+ }>
+ externalEmbed?: {
+ uri: string
+ title: string
+ description: string
+ thumb?: string // Proxied URL
+ }
+ // Links
+ postUrl: string
+ authorUrl: string
+ // Helpers
+ proxyImage: (url: string) => string
+}
+```
+
+### Named Slots
+
+| Slot | Description |
+|------|-------------|
+| `default` | Main content with slot props |
+| `loading` | Shown while fetching post data |
+| `error` | Shown if post fetch fails, receives `{ error }` |
+
+## How It Works
+
+1. **Server-side fetch**: The server fetches post data from `public.api.bsky.app` (AT Protocol) during SSR
+2. **Handle resolution**: The server resolves handles to DIDs for reliable post lookup
+3. **Image proxying**: The server rewrites all images to proxy through `/_scripts/embed/bluesky-image`
+4. **Rich text**: The component converts Bluesky facets (links, mentions, hashtags) to HTML
+5. **Caching**: The server caches responses for 10 minutes
+6. **No client-side API calls**: The user's browser never contacts Bluesky directly
+
+## Privacy Benefits
+
+- No third-party JavaScript loaded
+- No cookies set by Bluesky
+- No direct browser-to-Bluesky communication
+- User IP addresses not shared with Bluesky
+- All content served from your domain
+
+## Author Opt-Out
+
+The component respects Bluesky's `!no-unauthenticated` label. If a post author has opted out of external embedding, the API returns a 403 error and the component shows the error slot.
diff --git a/docs/content/scripts/ads/carbon-ads.md b/docs/content/scripts/carbon-ads.md
similarity index 80%
rename from docs/content/scripts/ads/carbon-ads.md
rename to docs/content/scripts/carbon-ads.md
index 5294f386..66d54283 100644
--- a/docs/content/scripts/ads/carbon-ads.md
+++ b/docs/content/scripts/carbon-ads.md
@@ -1,4 +1,5 @@
---
+
title: Carbon Ads
description: Show carbon ads in your Nuxt app using a Vue component.
links:
@@ -6,15 +7,22 @@ links:
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptCarbonAds.vue
size: xs
+
---
[Carbon Ads](https://www.carbonads.net/) is an ad service that provides a performance friendly way to show ads on your site.
-Nuxt Scripts provides a headless `ScriptCarbonAds` component to embed Carbon Ads in your Nuxt app.
+Nuxt Scripts provides a headless [``{lang="html"}](/scripts/carbon-ads){lang="html"} component to embed Carbon Ads in your Nuxt app.
+
+::script-stats
+::
+
+::script-types
+::
-## ScriptCarbonAds
+## [``{lang="html"}](/scripts/carbon-ads){lang="html"}
-The `ScriptCarbonAds` component works differently to other Nuxt Scripts component and does not rely on `useScript`, instead it simply
+The [``{lang="html"}](/scripts/carbon-ads){lang="html"} component works differently to other Nuxt Scripts component and does not rely on [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"}, instead it simply
inserts a script tag into the div of the component on mount.
By default, the component uses CarbonAds best practices which is to load immediately on mount. You can make use of [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) if you
@@ -25,6 +33,7 @@ want to load the ads on a specific event.
```
@@ -38,6 +47,7 @@ You can use these hooks to add a fallback when CarbonAds is blocked.
@@ -58,6 +68,7 @@ use this example from nuxt.com.
class="Carbon border border-gray-200 dark:border-gray-800 rounded-lg bg-white dark:bg-white/5"
serve="..."
placement="..."
+ format="..."
/>
@@ -161,12 +172,4 @@ use this example from nuxt.com.
See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-Note: The Carbon Ads script _does not_ extend the `useScript` composable. Accessing the script will return the `HTMLScriptElement`.
-
-### Props
-
-The `ScriptCarbonAds` component accepts the following props:
-
-- `serve`: The serve URL provided by Carbon Ads.
-- `placement`: The placement ID provided by Carbon Ads.
-
+Note: The Carbon Ads script _does not_ extend the [`useScript()`{lang="ts"}](/docs/api/use-script){lang="ts"} composable. Accessing the script will return the `HTMLScriptElement`.
diff --git a/docs/content/scripts/clarity.md b/docs/content/scripts/clarity.md
new file mode 100644
index 00000000..c4480e29
--- /dev/null
+++ b/docs/content/scripts/clarity.md
@@ -0,0 +1,22 @@
+---
+
+title: Clarity
+description: Use Clarity in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/clarity.ts
+ size: xs
+
+---
+
+[Clarity](https://clarity.microsoft.com/) by Microsoft is a screen recorder and heatmap tool that helps you understand how users interact with your website.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/cloudflare-web-analytics.md b/docs/content/scripts/cloudflare-web-analytics.md
new file mode 100644
index 00000000..83cc4997
--- /dev/null
+++ b/docs/content/scripts/cloudflare-web-analytics.md
@@ -0,0 +1,50 @@
+---
+
+title: Cloudflare Web Analytics
+description: Use Cloudflare Web Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/cloudflare-web-analytics.ts
+ size: xs
+
+---
+
+[Cloudflare Web Analytics](https://developers.cloudflare.com/analytics/web-analytics/) with Nuxt is a great privacy analytics solution. It offers free, privacy-centric analytics for your website. It doesn't gather personal data from your visitors, yet provides detailed insights into your web pages' performance as experienced by your visitors.
+
+::script-stats
+::
+
+::script-docs
+::
+
+The composable comes with the following defaults:
+- **Trigger: Client** Script will load when the Nuxt is hydrating to keep web vital metrics accurate.
+
+::script-types
+::
+
+## Loading in app.vue
+
+Loading Cloudflare Web Analytics through the `app.vue` when Nuxt is ready.
+
+```vue [app.vue]
+
+```
+
+The Cloudflare Web Analytics composable injects a `window.__cfBeacon` object into the global scope. If you need
+to access this you can do by awaiting the script.
+
+```ts
+const { onLoaded } = useScriptCloudflareWebAnalytics()
+onLoaded(({ cfBeacon }) => {
+ console.log(cfBeacon)
+})
+```
diff --git a/docs/content/scripts/content/google-maps.md b/docs/content/scripts/content/google-maps.md
deleted file mode 100644
index 17b85d6c..00000000
--- a/docs/content/scripts/content/google-maps.md
+++ /dev/null
@@ -1,384 +0,0 @@
----
-title: Google Maps
-description: Show performance-optimized Google Maps in your Nuxt app.
-links:
- - label: useScriptGoogleMaps
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-maps.ts
- size: xs
- - label: ""
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptGoogleMaps.vue
- size: xs
----
-
-[Google Maps](https://maps.google.com/) allows you to embed maps in your website and customize them with your content.
-
-Nuxt Scripts provides a `useScriptGoogleMaps` composable and a headless `ScriptGoogleMaps` component to interact with the Google Maps.
-
-## ScriptGoogleMaps
-
-The `ScriptGoogleMaps` component is a wrapper around the `useScriptGoogleMaps` composable. It provides a simple way to embed Google Maps in your Nuxt app.
-
-It's optimized for performance by leveraging the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading the Google Maps when specific elements events happen.
-
-Before Google Maps is loaded, it shows a placeholder using [Maps Static API](https://developers.google.com/maps/documentation/maps-static).
-
-By default, it will load on the `mouseover` and `mouseclick` events.
-
-### Billing & Permissions
-
-::callout
-You'll need an API key with permissions to access the [Static Maps API](https://developers.google.com/maps/documentation/maps-static/cloud-setup), the [Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/cloud-setup) and [Places API](https://developers.google.com/maps/documentation/places/web-service/cloud-setup).
-::
-
-Showing an interactive JS map requires the Maps JavaScript API, which is a paid service. If a user interacts with the map, the following costs will be incurred:
-- $7 per 1000 loads for the Maps JavaScript API (default for using Google Maps)
-- $2 per 1000 loads for the Static Maps API - You can avoid providing a `placeholder` slot.
-- $5 per 1000 loads for the Geocoding API - You can avoid this by providing a `google.maps.LatLng` object instead of a string for the `center` prop
-
-However, if the user never engages with the map, only the Static Maps API usage ($2 per 1000 loads) will be charged.
-
-Billing will be optimized in a [future update](https://github.com/nuxt/scripts/issues/83).
-
-You should consider using the [Iframe Embed](https://developers.google.com/maps/documentation/embed/get-started) instead if you want to avoid these costs
-and are okay with a less interactive map.
-
-### Demo
-
-::code-group
-
-:google-maps-demo{label="Output"}
-
-```vue [Input]
-
-
-
-
-
-```
-
-::
-
-### Props
-
-The `ScriptGoogleMaps` component accepts the following props:
-
-**Map**
-
-- `center`: Where to center the map. You can provide a string with the location or use a `{ lat: 0, lng: 0 }` object.
-- `apiKey`: The Google Maps API key. Must have access to the Static Maps API as well. You can optionally provide this as runtime config using the `public.scripts.googleMaps.apiKey` key.
-- `centerMarker`: Whether to display a marker at the center position. Default is `true`.
-- `mapOptions`: Options for the map. See [MapOptions](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions).
-
-**Placeholder**
-
-You can customize the placeholder image using the following props, alternatively, you can use the `#placeholder` slot to customize the placeholder image.
-
-- `placeholderOptions`: Customize the placeholder image attributes. See [Static Maps API](https://developers.google.com/maps/documentation/maps-static/start).
-- `placeholderAttrs`: Customize the placeholder image attributes.
-
-**Sizing**
-
-If you want to render a map larger than 640x640 you should provide your own placeholder as the [Static Maps API](https://developers.google.com/maps/documentation/maps-static/start)
-does not support rendering maps larger than this.
-
-- `width`: The width of the map. Default is `640`.
-- `height`: The height of the map. Default is `400`.
-
-**Optimizations**
-
-- `trigger`: The trigger event to load the Google Maps. Default is `mouseover`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
-- `aboveTheFold`: Optimizes the placeholder image for above-the-fold content. Default is `false`.
-
-**Markers**
-
-You can add markers to the static and interactive map by providing an array of `MarkerOptions`. See [MarkerOptions](https://developers.google.com/maps/documentation/javascript/reference/marker#MarkerOptions).
-
-- `markers`: An array of markers to display on the map.
-
-See the [markers](https://github.com/nuxt/scripts/blob/main/playground/pages/third-parties/google-maps/markers.vue) example for more information.
-
-### Guides
-
-#### Eager Loading Placeholder
-
-The Google Maps placeholder image is lazy-loaded by default. You should change this behavior if your map is above the fold
-or consider using the `#placeholder` slot to customize the placeholder image.
-
-::code-group
-
-```vue [Placeholder Attrs]
-
-```
-
-```vue [Placeholder Slot]
-
-
-
-
-
-```
-
-::
-
-#### Advanced Marker Control
-
-If you need more control over the markers on the map, you can use the exposed `createAdvancedMapMarker` function which
-will return the marker instance.
-
-```vue
-
-
-
-
-```
-
-
-#### Advanced Map Control
-
-The component exposes all internal APIs, so you can customize your map as needed.
-
-```vue
-
-
-
-
-```
-
-
-### Component API
-
-See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-
-### Events
-
-The `ScriptGoogleMaps` component emits a single `ready` event when the Google Maps is loaded.
-
-```ts
-const emits = defineEmits<{
- ready: [map: google.maps.Map]
-}>()
-```
-
-To subscribe to Google Map events, you can use the `ready` event.
-
-```vue
-
-
-
-
-
-```
-
-### Slots
-
-The component provides minimal UI by default, only enough to be functional and accessible. There are a number of slots for you to customize the maps however you like.
-
-**default**
-
-The default slot is used to display content that will always be visible.
-
-```vue
-
-
-
-
- My Custom Map
-
-
-
-
-```
-
-**awaitingLoad**
-
-The slot is used to display content while the Google Maps is loading.
-
-```vue
-
-
-
-
- Click to load the map!
-
-
-
-
-```
-
-**loading**
-
-The slot is used to display content while the Google Maps is loading.
-
-Note: This shows a `ScriptLoadingIndicator` by default for accessibility and UX, by providing a slot you will
-override this component. Make sure you provide a loading indicator.
-
-```vue
-
-
-
-
- Loading...
-
-
-
-
-```
-
-**placeholder**
-
-The slot is used to display a placeholder image before the Google Maps is loaded. By default, this will show the Google Maps Static API image for the map. You can display it however you like.
-
-```vue
-
-
-
-
-
-
-
-```
-
-## useScriptGoogleMaps
-
-The `useScriptGoogleMaps` composable lets you have fine-grain control over the Google Maps SDK. It provides a way to load the Google Maps SDK and interact with it programmatically.
-
-```ts
-export function useScriptGoogleMaps(_options?: GoogleMapsInput) {}
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### GoogleMapsApi
-
-```ts
-export interface GoogleMapsApi {
- // @types/google.maps
- maps: typeof google.maps
-}
-```
-
-## Example
-
-Loading the Google Maps SDK and interacting with it programmatically.
-
-```vue
-
-
-
-
-```
diff --git a/docs/content/scripts/crisp.md b/docs/content/scripts/crisp.md
new file mode 100644
index 00000000..e6b86b7a
--- /dev/null
+++ b/docs/content/scripts/crisp.md
@@ -0,0 +1,214 @@
+---
+
+title: Crisp
+description: Show performance-optimized Crisp in your Nuxt app.
+links:
+ - label: useScriptCrisp
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/crisp.ts
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptCrisp.vue
+ size: xs
+
+---
+
+[Crisp](https://crisp.chat/) is a customer messaging platform that lets you communicate with your customers through chat, email, and more.
+
+Nuxt Scripts provides a [`useScriptCrisp()`{lang="ts"}](#usescriptcrisp){lang="ts"} composable and a headless Facade Component [``{lang="html"}](#scriptcrisp){lang="html"} component to interact with crisp.
+
+::script-stats
+::
+
+::script-types
+::
+
+## [``{lang="html"}](/scripts/crisp){lang="html"}
+
+The [``{lang="html"}](/scripts/crisp){lang="html"} component is headless Facade Component wrapping the [`useScriptCrisp()`{lang="ts"}](#usescriptcrisp){lang="ts"} composable, providing a simple, performance optimized way to load Crisp in your Nuxt app.
+
+It's optimized for performance by using the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading crisp when specific elements events happen.
+
+By default, it will load on the `click` DOM event.
+
+### Demo
+
+::code-group
+
+:crisp-demo{label="Output"}
+
+```vue [Input]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+::
+
+### Component API
+
+See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
+
+#### With Environment Variables
+
+If you prefer to configure your id using environment variables.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ crisp: true,
+ }
+ },
+ // you need to provide a runtime config to access the environment variables
+ runtimeConfig: {
+ public: {
+ scripts: {
+ crisp: {
+ id: '', // NUXT_PUBLIC_SCRIPTS_CRISP_ID
+ },
+ },
+ },
+ },
+})
+```
+
+```text [.env]
+NUXT_PUBLIC_SCRIPTS_CRISP_ID=
+```
+
+### Events
+
+The [``{lang="html"}](/scripts/crisp){lang="html"} component emits a single `ready` event when Crisp loads.
+
+```ts
+const emits = defineEmits<{
+ ready: [crisp: Crisp]
+}>()
+```
+
+```vue
+
+
+
+
+
+```
+
+### Slots
+
+**awaitingLoad**
+
+This slot displays content while Crisp is loading.
+
+```vue
+
+
+
+
+ chat!
+
+
+
+
+```
+
+**loading**
+
+This slot displays content while Crisp is loading.
+
+Tip: You should use the `ScriptLoadingIndicator` by default for accessibility and UX.
+
+```vue
+
+
+
+
+ Loading...
+
+
+
+
+```
+
+## [`useScriptCrisp()`{lang="ts"}](/scripts/crisp){lang="ts"}
+
+The [`useScriptCrisp()`{lang="ts"}](/scripts/crisp){lang="ts"} composable lets you have fine-grain control over Crisp SDK. It provides a way to load crisp SDK and interact with it programmatically.
+
+```ts
+export function useScriptCrisp(_options?: CrispInput) {}
+```
+
+Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
+
+For more information, please refer to the [Crisp API documentation](https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/dollar-crisp/).
+
+## Example
+
+Loading the Crisp SDK and interacting with it programmatically.
+
+```vue
+
+```
diff --git a/docs/content/scripts/databuddy-analytics.md b/docs/content/scripts/databuddy-analytics.md
new file mode 100644
index 00000000..b80d30f9
--- /dev/null
+++ b/docs/content/scripts/databuddy-analytics.md
@@ -0,0 +1,34 @@
+---
+
+title: Databuddy Analytics
+description: Use Databuddy Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/databuddy-analytics.ts
+ size: xs
+
+---
+
+[Databuddy](https://www.databuddy.cc/) is a privacy-first analytics platform focused on performance and minimal data collection.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
+
+### CDN / Self-hosted
+
+By default the registry injects `https://cdn.databuddy.cc/databuddy.js`. If you host the script yourself, pass `scriptUrl` in options to override the `src`.
+
+```ts
+useScriptDatabuddyAnalytics({
+ scriptInput: { src: 'https://my-host/databuddy.js' },
+ clientId: 'YOUR_CLIENT_ID'
+})
+```
+
diff --git a/docs/content/scripts/fathom-analytics.md b/docs/content/scripts/fathom-analytics.md
new file mode 100644
index 00000000..2cb75e56
--- /dev/null
+++ b/docs/content/scripts/fathom-analytics.md
@@ -0,0 +1,62 @@
+---
+
+title: Fathom Analytics
+description: Use Fathom Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/fathom-analytics.ts
+ size: xs
+
+---
+
+[Fathom Analytics](https://usefathom.com/) is a great privacy analytics solution for your Nuxt app. It doesn't gather personal data from your visitors, yet provides detailed insights into how visitors use your site.
+
+::script-stats
+::
+
+::script-docs
+::
+
+## Defaults
+
+- **Trigger**: Script will load when Nuxt is hydrated.
+
+::script-types
+::
+
+You can access the `fathom` object as a proxy directly or await the `$script` promise to access the object. It's recommended
+to use the proxy for any void functions.
+
+::code-group
+
+```ts [Proxy]
+const { proxy } = useScriptFathomAnalytics()
+function trackMyGoal() {
+ proxy.trackGoal('MY_GOAL_ID', 100)
+}
+```
+
+```ts [onLoaded]
+const { onLoaded } = useScriptFathomAnalytics()
+onLoaded(({ trackGoal }) => {
+ trackGoal('MY_GOAL_ID', 100)
+})
+```
+
+::
+
+## Example
+
+Loading Fathom Analytics through the `app.vue` when Nuxt is ready.
+
+```vue [app.vue]
+
+```
diff --git a/docs/content/scripts/google-adsense.md b/docs/content/scripts/google-adsense.md
new file mode 100644
index 00000000..7380b3d7
--- /dev/null
+++ b/docs/content/scripts/google-adsense.md
@@ -0,0 +1,219 @@
+---
+
+title: Google Adsense
+description: Show Google Adsense ads in your Nuxt app.
+links:
+ - label: useScriptGoogleAdsense
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-adsense.ts
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptGoogleAdsense.vue
+ size: xs
+
+---
+
+[Google AdSense](https://www.google.com/adsense/start/) allows you to monetize your website by displaying relevant ads from Google.
+
+Nuxt Scripts provides:
+
+- [`useScriptGoogleAdsense()`{lang="ts"}](/scripts/google-adsense){lang="ts"}: A composable to manage Google AdSense dynamically.
+- ``{lang="html"}: A headless component to embed ads directly in your Nuxt app.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Global Setup
+
+You can configure Google AdSense **globally** in your `nuxt.config.ts` so that Nuxt automatically loads the script on all pages.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleAdsense: {
+ client: 'ca-pub-', // Your Google AdSense Publisher ID
+ autoAds: true, // Enable Auto Ads
+ },
+ },
+ },
+})
+```
+
+## Where to Find ``{lang="html"} (Publisher ID)
+
+Find your **Google AdSense Publisher ID** (also known as `ca-pub-XXXXXXX`) in your **Google AdSense Account**:
+
+1. Log in to your **Google AdSense** account.
+2. Navigate to **Account > Settings** (click on your profile icon > "Account information").
+3. Locate the **Publisher ID** under **Account Information**.
+4. Replace ``{lang="html"} in the config above with your actual ID.
+
+::callout{icon="i-heroicons-light-bulb" to="https://adsense.google.com/start/" target="_blank"}
+You can also manage **Auto Ads settings** from your **Google AdSense Dashboard** to control *ad types, placements, and revenue optimization*.
+::
+
+## Site Ownership Verification
+
+### Automatic Meta Tag Insertion
+
+If you provide a `client`, Nuxt automatically inserts a **meta tag** on the page for Google to verify your site ownership.
+
+::tabs
+ ::div
+ ---
+ label: Example
+ icon: i-heroicons-code-bracket-square
+ ---
+ ```ts [nuxt.config.ts]
+ export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleAdsense: {
+ client: 'ca-pub-', // AdSense Publisher ID
+ },
+ },
+ },
+ })
+ ```
+ ::
+ ::div
+ ---
+ label: Output
+ icon: i-heroicons-magnifying-glass-circle
+ ---
+ ```html
+
+ ```
+ ::
+::
+
+### Using `ads.txt` for Verification
+
+Google recommends adding an `ads.txt` file for **ad revenue eligibility**.
+
+#### Steps:
+
+1. Create a new file: `public/ads.txt`
+2. Add the following content:
+ ```plaintext
+ google.com, pub-, DIRECT, f08c47fec0942fa0
+ ```
+3. Replace ``{lang="html"} with your **AdSense Publisher ID**.
+
+::callout{icon="i-heroicons-light-bulb"}
+**Why use `ads.txt`?** It helps **prevent ad fraud** and ensures that **only your site** can display your ads.
+::
+
+## Enabling Auto Ads
+
+Auto Ads allow Google to **automatically** place ads for **better optimization**.
+
+::tabs
+ ::div
+ ---
+ label: Example
+ icon: i-heroicons-code-bracket-square
+ ---
+ ```ts [nuxt.config.ts]
+ export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleAdsense: {
+ client: 'ca-pub-', // AdSense Publisher ID
+ autoAds: true, // Enable Auto Ads
+ },
+ },
+ },
+ })
+ ```
+ ::
+ ::div
+ ---
+ label: Output
+ icon: i-heroicons-magnifying-glass-circle
+ ---
+ ```html
+
+ ```
+ ::
+::
+
+## Using [``{lang="html"}](/scripts/google-adsense){lang="html"} Component
+
+It provides a simple way to **embed ads** in your Nuxt app.
+
+```vue
+
+
+
+```
+
+### Component Props
+
+| Prop | Description |
+| ---------------------------- | --------------------------------------------------------------------- |
+| `data-ad-client` | Your **Google Adsense Publisher ID**(`ca-pub-XXXXXXXXXX`). |
+| `data-ad-slot` | Your **Ad Slot ID** (available in AdSense dashboard). |
+| `data-ad-format` | Ad format type (`auto`, `rectangle`, `horizontal`, `vertical`, `fluid`, `autorelaxed`). |
+| `data-ad-layout` | Layout (`in-article`, `in-feed`, `fixed`). |
+| `data-full-width-responsive` | **Set to `true`** to make the ad responsive. |
+
+#### Example Usage with `data-ad-layout`
+
+To specify a layout for your ads (such as "in-article"), you can use the `data-ad-layout` attribute:
+
+```vue
+
+
+
+```
+
+## How to Handle Ad-Blockers?
+
+If a user has an **ad-blocker enabled**, you can show **fallback content**.
+
+```vue
+
+
+
+
+
Please support us by disabling your ad blocker.
+
+
+
+```
+
+## Using [`useScriptGoogleAdsense()`{lang="ts"}](/scripts/google-adsense){lang="ts"} Composable
+
+The [`useScriptGoogleAdsense()`{lang="ts"}](/scripts/google-adsense){lang="ts"} composable allows **fine-grain control** over the AdSense script.
+
+```ts
+export function useScriptGoogleAdsense(
+ _options?: GoogleAdsenseInput
+) {}
+```
+
+See the [Registry Scripts Guide](/docs/guides/registry-scripts) for advanced usage.
+
+::callout{icon="i-heroicons-light-bulb" to="https://support.google.com/adsense" target="_blank"}
+Need more help? Check out the official **Google AdSense Guide**
+::
diff --git a/docs/content/scripts/google-analytics.md b/docs/content/scripts/google-analytics.md
new file mode 100644
index 00000000..d54cd46a
--- /dev/null
+++ b/docs/content/scripts/google-analytics.md
@@ -0,0 +1,158 @@
+---
+
+title: Google Analytics
+description: Use Google Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-analytics.ts
+ size: xs
+
+---
+
+[Google Analytics](https://marketingplatform.google.com/about/analytics/) is an analytics solution for Nuxt Apps.
+
+It provides detailed insights into how your website is performing, how users are interacting with your content, and how they are navigating through your site.
+
+::script-stats
+::
+
+::script-docs
+::
+
+### Usage
+
+To interact with the Google Analytics API, it's recommended to use script [proxy](/docs/guides/key-concepts#understanding-proxied-functions).
+
+```ts
+const { proxy } = useScriptGoogleAnalytics()
+
+proxy.gtag('event', 'page_view')
+```
+
+The proxy exposes the `gtag` and `dataLayer` properties, and you should use them following Google Analytics best practices.
+
+::script-types
+::
+
+### Customer/Consumer ID Tracking
+
+For e-commerce or multi-tenant applications where you need to track customer-specific analytics alongside your main tracking:
+
+```vue [ProductPage.vue]
+
+```
+
+## Custom Dimensions and User Properties
+
+```ts
+const { proxy } = useScriptGoogleAnalytics()
+
+// User properties (persist across sessions)
+proxy.gtag('set', 'user_properties', {
+ user_tier: 'premium',
+ account_type: 'business'
+})
+
+// Event with custom dimensions (register in GA4 Admin > Custom Definitions)
+proxy.gtag('event', 'purchase', {
+ transaction_id: 'T12345',
+ value: 99.99,
+ payment_method: 'credit_card', // custom dimension
+ discount_code: 'SAVE10' // custom dimension
+})
+
+// Default params for all future events
+proxy.gtag('set', { country: 'US', currency: 'USD' })
+```
+
+## Manual Page View Tracking (SPAs)
+
+GA4 auto-tracks page views. To disable and track manually:
+
+```ts
+const { proxy } = useScriptGoogleAnalytics()
+
+// Disable automatic page views
+proxy.gtag('config', 'G-XXXXXXXX', { send_page_view: false })
+
+// Track on route change
+const router = useRouter()
+router.afterEach((to) => {
+ proxy.gtag('event', 'page_view', { page_path: to.fullPath })
+})
+```
+
+## Proxy Queuing
+
+The proxy queues all `gtag` calls until the script loads. Calls are SSR-safe, adblocker-resilient, and order-preserved.
+
+```ts
+const { proxy, onLoaded } = useScriptGoogleAnalytics()
+
+// Fire-and-forget (queued until GA loads)
+proxy.gtag('event', 'cta_click', { button_id: 'hero-signup' })
+
+// Need return value? Wait for load
+onLoaded(({ gtag }) => {
+ gtag('get', 'G-XXXXXXXX', 'client_id', id => console.log(id))
+})
+```
+
+## Common Event Patterns
+
+```ts
+const { proxy } = useScriptGoogleAnalytics()
+
+// E-commerce
+proxy.gtag('event', 'purchase', {
+ transaction_id: 'T_12345',
+ value: 59.98,
+ currency: 'USD',
+ items: [{ item_id: 'SKU_12345', item_name: 'Widget', price: 29.99, quantity: 2 }]
+})
+
+// Engagement
+proxy.gtag('event', 'login', { method: 'Google' })
+proxy.gtag('event', 'search', { search_term: 'nuxt scripts' })
+
+// Custom
+proxy.gtag('event', 'feature_used', { feature_name: 'dark_mode' })
+```
+
+## Debugging
+
+Enable debug mode via config or URL param `?debug_mode=true`:
+
+```ts
+proxy.gtag('config', 'G-XXXXXXXX', { debug_mode: true })
+```
+
+View events in GA4: **Admin > DebugView**. Install [GA Debugger extension](https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna) for console logging.
+
+For consent mode setup, see the [Consent Guide](/docs/guides/consent).
diff --git a/docs/content/scripts/google-maps.md b/docs/content/scripts/google-maps.md
new file mode 100644
index 00000000..93cf292c
--- /dev/null
+++ b/docs/content/scripts/google-maps.md
@@ -0,0 +1,715 @@
+---
+
+title: Google Maps
+description: Show performance-optimized Google Maps in your Nuxt app.
+links:
+ - label: useScriptGoogleMaps
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-maps.ts
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptGoogleMaps.vue
+ size: xs
+
+---
+
+[Google Maps](https://maps.google.com/) allows you to embed maps in your website and customize them with your content.
+
+Nuxt Scripts provides a [`useScriptGoogleMaps()`{lang="ts"}](/scripts/google-maps){lang="ts"} composable and a headless [``{lang="html"}](/scripts/google-maps){lang="html"} component to interact with the Google Maps.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Types
+
+To use Google Maps with full TypeScript support, you will need
+to install the `@types/google.maps` dependency.
+
+```bash
+pnpm add -D @types/google.maps
+```
+
+## Setup
+
+To use the Google Maps component with server-side features (static map proxy, geocode proxy), enable it in your `nuxt.config`:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleMaps: true,
+ },
+ },
+})
+```
+
+This registers server API routes for the static maps image proxy (`/_scripts/proxy/google-static-maps`) and geocode proxy (`/_scripts/proxy/google-maps-geocode`), keeping your API key server-side.
+
+## [``{lang="html"}](/scripts/google-maps){lang="html"}
+
+The [``{lang="html"}](/scripts/google-maps){lang="html"} component is a wrapper around the [`useScriptGoogleMaps()`{lang="ts"}](/scripts/google-maps){lang="ts"} composable. It provides a simple way to embed Google Maps in your Nuxt app.
+
+It's optimized for performance by using the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading the Google Maps when specific elements events happen.
+
+Before Google Maps loads, it shows a placeholder using [Maps Static API](https://developers.google.com/maps/documentation/maps-static).
+
+By default, it will load on the `mouseover` and `mouseclick` events.
+
+### Billing & Permissions
+
+::callout
+You'll need an API key with permissions to access the [Maps JavaScript API](https://developers.google.com/maps/documentation/javascript/cloud-setup).
+
+Optionally, you can provide permissions to the [Static Maps API](https://developers.google.com/maps/documentation/maps-static/cloud-setup) (required when lazy loading and using the placeholder map) and [Places API](https://developers.google.com/maps/documentation/places/web-service/cloud-setup) (required when searching using a query, i.e "New York").
+::
+
+Showing an interactive JS map requires the Maps JavaScript API, which is a paid service. If a user interacts with the map, the following costs will be incurred:
+- $7 per 1000 loads for the Maps JavaScript API (default for using Google Maps)
+- $2 per 1000 loads for the Static Maps API - Only used when you don't provide a `placeholder` slot.
+- $5 per 1000 loads for the Geocoding API - Only used when you don't provide a `google.maps.LatLng` object instead of a query string for the `center` prop
+
+However, if the user never engages with the map, only the Static Maps API usage ($2 per 1000 loads) will be charged, assuming you're using it.
+
+Billing will be optimized in a [future update](https://github.com/nuxt/scripts/issues/83).
+
+You should consider using the [Iframe Embed](https://developers.google.com/maps/documentation/embed/get-started) instead if you want to avoid these costs
+and are okay with a less interactive map.
+
+### Demo
+
+::code-group
+
+:google-maps-demo{label="Output"}
+
+```vue [Input]
+
+
+
+
+
+```
+
+::
+
+#### With Environment Variables
+
+If you prefer to configure your API key using environment variables.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleMaps: true,
+ }
+ },
+ // you need to provide a runtime config to access the environment variables
+ runtimeConfig: {
+ public: {
+ scripts: {
+ googleMaps: {
+ apiKey: '', // NUXT_PUBLIC_SCRIPTS_GOOGLE_MAPS_API_KEY
+ },
+ },
+ },
+ },
+})
+```
+
+```text [.env]
+NUXT_PUBLIC_SCRIPTS_GOOGLE_MAPS_API_KEY=
+```
+
+### Guides
+
+#### Eager Loading Placeholder
+
+The Google Maps placeholder image is lazy-loaded by default. You should change this behavior if your map is above the fold
+or consider using the `#placeholder` slot to customize the placeholder image.
+
+::code-group
+
+```vue [Placeholder Attrs]
+
+```
+
+```vue [Placeholder Slot]
+
+
+
+
+
+```
+
+::
+
+#### Advanced Marker Control
+
+If you need more control over the markers on the map, you can use the exposed `createAdvancedMapMarker` function which
+will return the marker instance.
+
+```vue
+
+
+
+
+
+```
+
+
+#### Advanced Map Control
+
+The component exposes all internal APIs, so you can customize your map as needed.
+
+```vue
+
+
+
+
+
+```
+
+#### Loading immediately
+
+If you want to load the Google Maps immediately, you can use the `trigger` prop.
+
+```vue
+
+
+
+```
+
+#### Map Styling
+
+You can style the map by using the `mapOptions.styles` prop. You can find pre-made styles on [Snazzy Maps](https://snazzymaps.com/).
+
+This will automatically work for both the static map placeholder and the interactive map.
+
+```vue
+
+
+
+
+
+```
+
+### Component API
+
+See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
+
+### Events
+
+The [``{lang="html"}](/scripts/google-maps){lang="html"} component emits a single `ready` event when Google Maps loads.
+
+```ts
+const emits = defineEmits<{
+ ready: [map: google.maps.Map]
+}>()
+```
+
+To subscribe to Google Map events, you can use the `ready` event.
+
+```vue
+
+
+
+
+
+```
+
+### Slots
+
+The component provides minimal UI by default, only enough to be functional and accessible. There are a number of slots for you to customize the maps however you like.
+
+**default**
+
+The default slot displays content that will always be visible.
+
+```vue
+
+
+
+
+ My Custom Map
+
+
+
+
+```
+
+**awaitingLoad**
+
+This slot displays content while Google Maps is loading.
+
+```vue
+
+
+
+
+ Click to load the map!
+
+
+
+
+```
+
+**loading**
+
+This slot displays content while Google Maps is loading.
+
+Note: This shows a `ScriptLoadingIndicator` by default for accessibility and UX, by providing a slot you will
+override this component. Make sure you provide a loading indicator.
+
+```vue
+
+
+
+
+ Loading...
+
+
+
+
+```
+
+**placeholder**
+
+This slot displays a placeholder image before Google Maps loads. By default, this will show the Google Maps Static API image for the map.
+
+By providing your own placeholder slot, you disable the default placeholder image and won't incur charges for the Static Maps API.
+
+```vue
+
+
+
+
+
+
+
+```
+
+## Google Maps SFC Components
+
+Nuxt Scripts provides individual Single File Components (SFCs) for different Google Maps elements. These components allow you to declaratively compose complex maps using Vue's template syntax.
+
+### Installation
+
+To use marker clustering functionality, you'll need to install the required peer dependency:
+
+```bash
+npm install @googlemaps/markerclusterer
+# or
+yarn add @googlemaps/markerclusterer
+# or
+pnpm add @googlemaps/markerclusterer
+```
+
+### Available Components
+
+All Google Maps SFC components must work within a ``{lang="html"} component:
+
+- ``{lang="html"} - Classic markers with icon support
+- ``{lang="html"} - Modern advanced markers with HTML content
+- ``{lang="html"} - Customizable pin markers (use within AdvancedMarkerElement)
+- ``{lang="html"} - Information windows that appear on click
+- ``{lang="html"} - Groups nearby markers into clusters
+- ``{lang="html"} - Circular overlays
+- ``{lang="html"} - Polygon shapes
+- ``{lang="html"} - Line paths
+- ``{lang="html"} - Rectangular overlays
+- ``{lang="html"} - Heatmap visualization
+
+### Basic Usage
+
+```vue
+
+
+
+
+
+
+
+```
+
+## Test Keys
+
+Google provides test keys for development that always pass verification. Use these for local testing:
+
+| Key Type | Value |
+|----------|-------|
+| Site Key | `6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI` |
+| Secret Key | `6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe` |
+
+::callout{type="info"}
+Test keys will always return `success: true` with a score of `0.9`. See [Google's FAQ](https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do) for more details.
+::
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ $development: {
+ scripts: {
+ registry: {
+ googleRecaptcha: {
+ siteKey: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'
+ }
+ }
+ }
+ }
+})
+```
diff --git a/docs/content/scripts/google-sign-in.md b/docs/content/scripts/google-sign-in.md
new file mode 100644
index 00000000..ce03623a
--- /dev/null
+++ b/docs/content/scripts/google-sign-in.md
@@ -0,0 +1,273 @@
+---
+
+title: Google Sign-In
+description: Add Google Sign-In to your Nuxt app with One Tap and personalized button support.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-sign-in.ts
+ size: xs
+- label: Google Identity Services
+ icon: i-simple-icons-google
+ to: https://developers.google.com/identity/gsi/web/guides/overview
+ size: xs
+
+---
+
+[Google Sign-In](https://developers.google.com/identity/gsi/web) provides a secure and convenient way for users to sign in to your app using their Google Account with One Tap, personalized buttons, and automatic sign-in.
+
+Nuxt Scripts provides a registry script composable [`useScriptGoogleSignIn()`{lang="ts"}](/scripts/google-sign-in){lang="ts"} to easily integrate Google Sign-In in your Nuxt app with optimal performance.
+
+::script-stats
+::
+
+## Live Demo
+
+::google-sign-in-demo
+::
+
+::script-docs{:sections='["setup", "composable"]'}
+::
+
+::script-types
+::
+
+## Example
+
+### One Tap Sign-In
+
+The One Tap prompt provides a simplified sign-in experience:
+
+```vue
+
+```
+
+### Personalized Button
+
+Render Google's personalized Sign in with Google button:
+
+```vue
+
+
+
+
+
+```
+
+## Moment Notifications
+
+Track the One Tap display state:
+
+```ts
+const { onLoaded } = useScriptGoogleSignIn()
+
+onLoaded(({ accounts }) => {
+ accounts.id.prompt((notification) => {
+ if (notification.isDisplayMoment()) {
+ if (notification.isDisplayed()) {
+ console.log('One Tap displayed')
+ }
+ else {
+ console.log('Not displayed:', notification.getNotDisplayedReason())
+ }
+ }
+
+ if (notification.isSkippedMoment()) {
+ console.log('Skipped:', notification.getSkippedReason())
+ }
+
+ if (notification.isDismissedMoment()) {
+ console.log('Dismissed:', notification.getDismissedReason())
+ }
+ })
+})
+```
+
+## Server-Side Verification
+
+Always verify the credential token on your server:
+
+```ts [server/api/auth/google.post.ts]
+export default defineEventHandler(async (event) => {
+ const { credential } = await readBody(event)
+
+ // Verify the token with Google
+ const response = await $fetch(`https://oauth2.googleapis.com/tokeninfo`, {
+ params: { id_token: credential }
+ })
+
+ // Verify the client ID matches
+ if (response.aud !== 'YOUR_CLIENT_ID') {
+ throw createError({ statusCode: 401, message: 'Invalid token' })
+ }
+
+ // Create session with user info
+ const user = {
+ email: response.email,
+ name: response.name,
+ picture: response.picture,
+ sub: response.sub
+ }
+
+ return { user }
+})
+```
+
+## FedCM API Support
+
+Enable Privacy Sandbox [FedCM API](https://developers.google.com/identity/gsi/web/guides/fedcm-migration) support for enhanced privacy. **FedCM adoption becomes mandatory in August 2025.**
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleSignIn: {
+ clientId: 'YOUR_CLIENT_ID',
+ useFedcmForPrompt: true
+ }
+ }
+ }
+})
+```
+
+### Cross-Origin Iframes
+
+When using One Tap or the Sign-In button in cross-origin iframes with FedCM, add the `allow` attribute to all parent iframes:
+
+```html
+
+```
+
+::warning
+With FedCM enabled, customizing the One Tap prompt position via `prompt_parent_id` is not supported.
+::
+
+## Revoking Access
+
+Allow users to revoke access to their Google Account:
+
+```ts
+const { onLoaded } = useScriptGoogleSignIn()
+
+function revokeAccess(userId: string) {
+ onLoaded(({ accounts }) => {
+ accounts.id.revoke(userId, (response) => {
+ if (response.successful) {
+ console.log('Access revoked')
+ }
+ else {
+ console.error('Revocation failed:', response.error)
+ }
+ })
+ })
+}
+```
+
+## Best Practices
+
+### Logout Handling
+
+Always call `disableAutoSelect()`{lang="ts"} when the user signs out to prevent automatic re-authentication:
+
+```ts
+function signOut() {
+ // Clear your app's session
+ user.value = null
+
+ // Prevent One Tap from auto-selecting this account
+ onLoaded(({ accounts }) => {
+ accounts.id.disableAutoSelect()
+ })
+}
+```
+
+### Hosted Domain Restriction
+
+Restrict sign-in to a specific Google Workspace domain:
+
+```ts
+accounts.id.initialize({
+ client_id: 'YOUR_CLIENT_ID',
+ callback: handleCredentialResponse,
+ hd: 'your-company.com' // Only allow users from this domain
+})
+```
+
+## Local Development Setup
+
+To test Google Sign-In locally:
+
+1. Go to [Google Cloud Console → Credentials](https://console.cloud.google.com/apis/credentials)
+2. Create or select an OAuth 2.0 Client ID (Web application type)
+3. Under **Authorized JavaScript origins**, add:
+ - `http://localhost`
+ - `http://localhost:3000` (or your dev server port)
+4. Save and copy your Client ID
+
+::note
+Google requires `http://localhost` (not `127.0.0.1`) for local development. You don't need a redirect URI when using popup mode.
+::
+
+Then configure your environment:
+
+```bash [.env]
+NUXT_PUBLIC_SCRIPTS_GOOGLE_SIGN_IN_CLIENT_ID=your-client-id.apps.googleusercontent.com
+```
+
+## Guides
+
+::note
+For more detailed info on how to obtain a Google Client ID and configure your OAuth consent screen, see the official [Google Identity Services documentation](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid).
+::
diff --git a/docs/content/scripts/google-tag-manager.md b/docs/content/scripts/google-tag-manager.md
new file mode 100644
index 00000000..5106959c
--- /dev/null
+++ b/docs/content/scripts/google-tag-manager.md
@@ -0,0 +1,176 @@
+---
+
+title: Google Tag Manager
+description: Use Google Tag Manager in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-tag-manager.ts
+ size: xs
+
+---
+
+[Google Tag Manager](https://marketingplatform.google.com/about/tag-manager/) is a tag management system that allows you to quickly and easily update tags and code snippets on your website or mobile app, such as those intended for traffic analysis and marketing optimization.
+
+::callout
+You may not need Google Tag Manager with Nuxt Scripts. GTM is 82kb and will slow down your site.
+Nuxt Scripts provides many features you can easily
+implement within your Nuxt app. If you're using GTM for Google Analytics, you can use the [`useScriptGoogleAnalytics()`{lang="ts"}](/scripts/google-analytics){lang="ts"} composable instead.
+::
+
+::script-stats
+::
+
+::script-docs
+::
+
+### Guide: Sending Page Events
+
+If you'd like to manually send page events to Google Tag Manager, you can use the `proxy` with the [`useScriptEventPage()`{lang="ts"}](/docs/api/use-script-event-page){lang="ts"} composable.
+This composable triggers the provided function on route change after Nuxt updates the page title.
+
+```ts
+const { proxy } = useScriptGoogleTagManager({
+ id: 'YOUR_ID' // id is only needed if you haven't configured globally
+})
+
+useScriptEventPage(({ title, path }) => {
+ // triggered on route change after title is updated
+ proxy.dataLayer.push({
+ event: 'pageview',
+ title,
+ path
+ })
+})
+```
+
+::script-types
+::
+
+## Examples
+
+### Server-Side GTM Setup
+
+Server-side GTM moves tag execution to your server for better privacy, performance (~500ms faster), and ad-blocker bypass.
+
+**Prerequisites:** [Server-side GTM container](https://tagmanager.google.com), hosting ([Cloud Run](https://developers.google.com/tag-platform/tag-manager/server-side/cloud-run-setup-guide) / [Docker](https://developers.google.com/tag-platform/tag-manager/server-side/manual-setup-guide)), and a custom domain.
+
+#### Configuration
+
+Override the script source with your custom domain:
+
+```ts
+// nuxt.config.ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ googleTagManager: {
+ id: 'GTM-XXXXXX',
+ scriptInput: {
+ src: 'https://gtm.example.com/gtm.js'
+ }
+ }
+ }
+ }
+})
+```
+
+For environment tokens (`auth`, `preview`), find them in GTM: Admin > Environments > Get Snippet.
+
+#### Troubleshooting
+
+| Issue | Cause | Solution |
+|-------|-------|----------|
+| Script blocked by ad blocker | Custom domain detected as tracker | Use a non-obvious subdomain name (avoid `gtm`, `analytics`, `tracking`) |
+| Cookies expire after 7 days in Safari | ITP treats subdomain as third-party | Use same-origin setup or implement cookie keeper |
+| Preview mode not working | Missing or incorrect auth/preview tokens | Copy tokens from GTM: Admin > Environments > Get Snippet |
+| CORS errors | Server container misconfigured | Ensure your server container allows requests from your domain |
+| `gtm.js` returns 404 | Incorrect path mapping | Verify your CDN/proxy routes `/gtm.js` to the container |
+
+For infrastructure setup, see [Cloud Run](https://developers.google.com/tag-platform/tag-manager/server-side/cloud-run-setup-guide) or [Docker](https://developers.google.com/tag-platform/tag-manager/server-side/manual-setup-guide) guides.
+
+## Configuring GTM before it starts
+
+[`useScriptGoogleTagManager()`{lang="ts"}](/scripts/google-tag-manager){lang="ts"} initializes Google Tag Manager by itself. This means it pushes the `js`, `config` and the `gtm.start` events for you.
+
+If you need to configure GTM before it starts, for example [setting the consent mode](https://developers.google.com/tag-platform/security/guides/consent?consentmode=basic), you have two options:
+
+### Option 1: Using `defaultConsent` in nuxt.config (Recommended)
+
+If you're configuring GTM in `nuxt.config`, use the `defaultConsent` option. See the [Default consent mode](#loading-globally) example above.
+
+### Option 2: Using `onBeforeGtmStart` callback
+
+If you're calling [`useScriptGoogleTagManager()`{lang="ts"}](/scripts/google-tag-manager){lang="ts"} with the ID directly in a component (not in nuxt.config), use the `onBeforeGtmStart` hook which runs right before the `gtm.start` event is pushed.
+
+::callout{icon="i-heroicons-exclamation-triangle" color="warning"}
+`onBeforeGtmStart` only works when the GTM ID is passed directly to [`useScriptGoogleTagManager()`{lang="ts"}](/scripts/google-tag-manager){lang="ts"}, not when configured globally in nuxt.config. For global config, use the `defaultConsent` option instead.
+::
+
+::callout{icon="i-heroicons-play" to="https://stackblitz.com/github/nuxt/scripts/tree/main/examples/cookie-consent" target="_blank"}
+Try the live [Cookie Consent Example](https://stackblitz.com/github/nuxt/scripts/tree/main/examples/cookie-consent) or [Granular Consent Example](https://stackblitz.com/github/nuxt/scripts/tree/main/examples/granular-consent) on [StackBlitz](https://stackblitz.com).
+::
+
+#### Consent Mode v2 Signals
+
+| Signal | Purpose |
+|--------|---------|
+| `ad_storage` | Cookies for advertising |
+| `ad_user_data` | Send user data to Google for ads |
+| `ad_personalization` | Personalized ads (remarketing) |
+| `analytics_storage` | Cookies for analytics |
+
+#### Updating Consent
+
+When the user accepts, call `gtag('consent', 'update', ...)`{lang="ts"}:
+
+```ts
+function acceptCookies() {
+ window.gtag?.('consent', 'update', {
+ ad_storage: 'granted',
+ ad_user_data: 'granted',
+ ad_personalization: 'granted',
+ analytics_storage: 'granted',
+ })
+}
+```
+
+To block GTM until consent, combine with [`useScriptTriggerConsent()`{lang="ts"}](/docs/guides/consent){lang="ts"}.
+
+```vue
+
+```
diff --git a/docs/content/scripts/gravatar.md b/docs/content/scripts/gravatar.md
new file mode 100644
index 00000000..2910dca6
--- /dev/null
+++ b/docs/content/scripts/gravatar.md
@@ -0,0 +1,26 @@
+---
+
+title: Gravatar
+description: Use Gravatar in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/gravatar.ts
+ size: xs
+- label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptGravatar.vue
+ size: xs
+
+---
+
+[Gravatar](https://gravatar.com) provides globally recognized avatars linked to email addresses. Nuxt Scripts provides a privacy-preserving integration that proxies avatar requests through your own server, preventing Gravatar from tracking your users.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/hotjar.md b/docs/content/scripts/hotjar.md
new file mode 100644
index 00000000..cc193529
--- /dev/null
+++ b/docs/content/scripts/hotjar.md
@@ -0,0 +1,22 @@
+---
+
+title: Hotjar
+description: Use Hotjar in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/hotjar.ts
+ size: xs
+
+---
+
+[Hotjar](https://www.hotjar.com/) is a screen recorder and heatmap tool that helps you understand how users interact with your website.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/instagram-embed.md b/docs/content/scripts/instagram-embed.md
new file mode 100644
index 00000000..417e8bc5
--- /dev/null
+++ b/docs/content/scripts/instagram-embed.md
@@ -0,0 +1,148 @@
+---
+
+title: Instagram Embed
+description: Server-side rendered Instagram embeds with zero client-side API calls.
+links:
+ - label: ScriptInstagramEmbed
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptInstagramEmbed.vue
+ size: xs
+
+---
+
+[Instagram](https://instagram.com) is a photo and video sharing social media platform.
+
+Nuxt Scripts provides a [``{lang="html"}](/scripts/instagram-embed){lang="html"} component that fetches Instagram embed HTML server-side and proxies all assets through your server - no client-side API calls to Instagram.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Setup
+
+To use the Instagram embed component, you must enable it in your `nuxt.config`:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ instagramEmbed: true,
+ },
+ },
+})
+```
+
+This registers the required server API routes (`/_scripts/embed/instagram`, `/_scripts/embed/instagram-image`, and `/_scripts/embed/instagram-asset`) that handle fetching embed HTML and proxying images/assets.
+
+## [``{lang="html"}](/scripts/instagram-embed){lang="html"}
+
+The [``{lang="html"}](/scripts/instagram-embed){lang="html"} component:
+- Fetches the official Instagram embed HTML server-side
+- Rewrites all image and asset URLs to proxy through your server
+- Removes Instagram's embed.js script (not needed)
+- Caches responses for 10 minutes
+
+### Demo
+
+::code-group
+
+:instagram-embed-demo{label="Output"}
+
+```vue [Basic Usage]
+
+
+
+```
+
+```vue [With Custom Loading/Error States]
+
+
+
+
+
+
+
+```
+
+::
+
+### Slot Props
+
+The default slot receives:
+
+```ts
+interface SlotProps {
+ html: string // The processed embed HTML
+ shortcode: string // The post shortcode (e.g., "C3Sk6d2MTjI")
+ postUrl: string // The original post URL
+}
+```
+
+### Named Slots
+
+| Slot | Description |
+|------|-------------|
+| `default` | Main content, receives `{ html, shortcode, postUrl }`. By default renders the HTML. |
+| `loading` | Shown while fetching embed HTML |
+| `error` | Shown if embed fetch fails, receives `{ error }` |
+
+## Supported URL Formats
+
+- Posts: `https://www.instagram.com/p/ABC123/`
+- Reels: `https://www.instagram.com/reel/ABC123/`
+- TV: `https://www.instagram.com/tv/ABC123/`
+
+## How It Works
+
+1. **Server-side fetch**: Nuxt fetches the Instagram embed HTML from `{postUrl}/embed/`
+2. **Asset proxying**: All images from `scontent.cdninstagram.com` and assets from `static.cdninstagram.com` are rewritten to proxy through your server
+3. **Script removal**: Nuxt removes Instagram's `embed.js` (not needed for static rendering)
+4. **Caching**: Nuxt caches responses for 10 minutes at the server level
+
+This approach is inspired by [Cloudflare Zaraz's embed implementation](https://blog.cloudflare.com/zaraz-supports-server-side-rendering-of-embeds/).
+
+## Privacy Benefits
+
+- No third-party JavaScript loaded
+- No cookies set by Instagram/Meta
+- No direct browser-to-Instagram communication
+- User IP addresses not shared with Instagram
+- All content served from your domain
+
+## Limitations
+
+- Only supports single-image posts (galleries show first image only)
+- Videos display as static poster images
+- Some interactive features are not available (likes, comments)
diff --git a/docs/content/scripts/intercom.md b/docs/content/scripts/intercom.md
new file mode 100644
index 00000000..4fd71937
--- /dev/null
+++ b/docs/content/scripts/intercom.md
@@ -0,0 +1,237 @@
+---
+
+title: Intercom
+description: Use Intercom in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/intercom.ts
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptIntercom.vue
+ size: xs
+
+---
+
+[Intercom](https://www.intercom.com/) is a customer messaging platform that helps you build better customer relationships.
+
+Nuxt Scripts provides a [`useScriptIntercom()`{lang="ts"}](#usescriptintercom){lang="ts"} composable and a headless Facade Component [``{lang="html"}](#scriptintercom){lang="html"} component to interact with Intercom.
+
+::script-stats
+::
+
+::script-types
+::
+
+## [``{lang="html"}](/scripts/intercom){lang="html"}
+
+
+The [``{lang="html"}](/scripts/intercom){lang="html"} component is headless Facade Component wrapping the [`useScriptIntercom()`{lang="ts"}](#usescriptintercom){lang="ts"} composable, providing a simple, performance optimized way to load Intercom in your Nuxt app.
+
+It's optimized for performance by using the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading Intercom when specific elements events happen.
+
+By default, it will load on the `click` DOM event.
+
+### Demo
+
+::code-group
+
+:intercom-demo{label="Output"}
+
+```vue [Input]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+::
+
+### Component API
+
+See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
+
+#### With Environment Variables
+
+If you prefer to configure your app ID using environment variables.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ intercom: true,
+ }
+ },
+ // you need to provide a runtime config to access the environment variables
+ runtimeConfig: {
+ public: {
+ scripts: {
+ intercom: {
+ app_id: '', // NUXT_PUBLIC_SCRIPTS_INTERCOM_APP_ID
+ },
+ },
+ },
+ },
+})
+```
+
+```text [.env]
+NUXT_PUBLIC_SCRIPTS_INTERCOM_APP_ID=
+```
+
+### Events
+
+The [``{lang="html"}](/scripts/intercom){lang="html"} component emits a single `ready` event when Intercom loads.
+
+```ts
+const emits = defineEmits<{
+ ready: [intercom: Intercom]
+}>()
+```
+
+```vue
+
+
+
+
+
+```
+
+### Intercom API
+
+The component exposes a `intercom` instance that you can access the basic Intercom API.
+
+```vue
+
+
+
+
+
+```
+
+### Slots
+
+The component provides minimal UI by default, only enough to be functional and accessible. There are a number of slots for you to customize the maps however you like.
+
+**default**
+
+The default slot displays content that will always be visible.
+
+**awaitingLoad**
+
+This slot displays content while Intercom is not loading.
+
+```vue
+
+
+
+
+ chat!
+
+
+
+
+```
+
+**loading**
+
+This slot displays content while Intercom is loading.
+
+Tip: You should use the `ScriptLoadingIndicator` by default for accessibility and UX.
+
+```vue
+
+
+
+
+ Loading...
+
+
+
+
+```
+
+
+## [`useScriptIntercom()`{lang="ts"}](/scripts/intercom){lang="ts"}
+
+The [`useScriptIntercom()`{lang="ts"}](/scripts/intercom){lang="ts"} composable lets you have fine-grain control over when and how Intercom loads on your site.
+
+```ts
+const { proxy } = useScriptIntercom({
+ app_id: 'YOUR_APP_ID'
+})
+
+// examples
+proxy.Intercom('show')
+proxy.Intercom('update', { name: 'John Doe' })
+```
+
+Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
+
+## Example
+
+Using Intercom only in production.
+
+::code-group
+
+```vue [IntercomButton.vue]
+
+
+
+
+
+ Chat with us
+
+
+
+```
+
+
+::
diff --git a/docs/content/scripts/lemon-squeezy.md b/docs/content/scripts/lemon-squeezy.md
new file mode 100644
index 00000000..c04ae01c
--- /dev/null
+++ b/docs/content/scripts/lemon-squeezy.md
@@ -0,0 +1,133 @@
+---
+
+title: Lemon Squeezy
+description: Use Lemon Squeezy in your Nuxt app.
+links:
+ - label: useScriptLemonSqueezy
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/lemon-squeezy.ts
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptLemonSqueezy.vue
+ size: xs
+
+---
+
+[Lemon Squeezy](https://www.lemonsqueezy.com/) is a popular payment gateway that allows you to accept payments online.
+
+Nuxt Scripts provides a [`useScriptLemonSqueezy()`{lang="ts"}](#usescriptlemonsqueezy){lang="ts"} composable and a headless Facade Component [``{lang="html"}](#scriptlemonsqueezy){lang="html"} component to interact with lemon squeezy.
+
+
+::script-stats
+::
+
+::script-types
+::
+
+## [``{lang="html"}](/scripts/lemon-squeezy){lang="html"}
+
+The [``{lang="html"}](/scripts/lemon-squeezy){lang="html"} component is headless [Facade Component](/docs/guides/facade-components) wrapping the [`useScriptLemonSqueezy()`{lang="ts"}](/scripts/lemon-squeezy){lang="ts"} composable, providing a simple, performance optimized way to load Lemon Squeezy in your Nuxt app.
+
+```vue
+
+
+
+ Buy me - $9.99
+
+
+
+```
+
+It works by injecting a `.lemonsqueezy-button` class onto any `a` tags within the component then loading in
+the Lemon Squeezy script with the `visibility` [Element Event Trigger](/docs/guides/script-triggers#element-event-triggers).
+
+### Demo
+
+::code-group
+
+:lemon-squeezy-demo{label="Output"}
+
+```vue [Input]
+
+
+
+
+
+ events.push(e)" @ready="ready = true">
+
+ Buy me - $9.99
+
+
+ Buy me - pay what you want
+
+
+
+
+
+
+
+
+ Buttons are live and will open the modal, tracking events:
+
+
+ {{ event.event }}
+
+
+
+
+
+
+```
+
+::
+
+### Component API
+
+See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
+
+### Events
+
+***`lemon-squeezy-event`***
+
+Events emitted by the Lemon.js script are forwarded through this event. The payload is an object with an `event` key and a `data` key.
+
+```ts
+export type LemonSqueezyEventPayload = { event: 'Checkout.Success', data: Record }
+ & { event: 'Checkout.ViewCart', data: Record }
+ & { event: 'GA.ViewCart', data: Record }
+ & { event: 'PaymentMethodUpdate.Mounted' }
+ & { event: 'PaymentMethodUpdate.Closed' }
+ & { event: 'PaymentMethodUpdate.Updated' }
+ & { event: string }
+ ```
+
+## [`useScriptLemonSqueezy()`{lang="ts"}](/scripts/lemon-squeezy){lang="ts"}
+
+The [`useScriptLemonSqueezy()`{lang="ts"}](/scripts/lemon-squeezy){lang="ts"} composable lets you have fine-grain control over the Lemon Squeezy SDK. It provides a way to load the Lemon Squeezy SDK and interact with it programmatically.
+
+```ts
+export function useScriptLemonSqueezy(_options?: LemonSqueezyInput) {}
+```
+
+Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
+
+## Example
+
+Using the Lemon Squeezy SDK with a payment link.
+
+```vue
+
+
+
+ Buy now
+
+```
diff --git a/docs/content/scripts/marketing/clarity.md b/docs/content/scripts/marketing/clarity.md
deleted file mode 100644
index 41b3120b..00000000
--- a/docs/content/scripts/marketing/clarity.md
+++ /dev/null
@@ -1,155 +0,0 @@
----
-title: Clarity
-description: Use Clarity in your Nuxt app.
-links:
-- label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/clarity.ts
- size: xs
----
-
-[Clarity](https://clarity.microsoft.com/) by Microsoft is a screen recorder and heatmap tool that helps you understand how users interact with your website.
-
-Nuxt Scripts provides a registry script composable `useScriptClarity` to easily integrate Clarity in your Nuxt app.
-
-The simplest way to load Clarity globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptClarity](#useScriptClarity) composable.
-
-## Loading Globally
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- clarity: {
- id: 'YOUR_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- clarity: {
- id: 'YOUR_ID',
- }
- }
- }
- }
-})
-```
-
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- clarity: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- clarity: {
- // .env
- // NUXT_PUBLIC_SCRIPTS_CLARITY_ID=
- id: '',
- },
- },
- },
- },
-})
-```
-
-::
-
-## useScriptClarity
-
-The `useScriptClarity` composable lets you have fine-grain control over when and how Clarity is loaded on your site.
-
-```ts
-const { proxy } = useScriptClarity({
- id: 'YOUR_ID'
-})
-// example
-proxy.clarity("identify", "custom-id", "custom-session-id", "custom-page-id", "friendly-name")
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### ClarityApi
-
-```ts
-type ClarityFunctions = ((fn: 'start', options: { content: boolean, cookies: string[], dob: number, expire: number, projectId: string, upload: string }) => void)
- & ((fn: 'identify', id: string, session?: string, page?: string, userHint?: string) => Promise<{
- id: string
- session: string
- page: string
- userHint: string
-}>)
- & ((fn: 'consent') => void)
- & ((fn: 'set', key: any, value: any) => void)
- & ((fn: 'event', value: any) => void)
- & ((fn: 'upgrade', upgradeReason: any) => void)
- & ((fn: string, ...args: any[]) => void)
-
-export interface ClarityApi {
- clarity: ClarityFunctions & {
- q: any[]
- v: string
- }
-}
-
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const ClarityOptions = object({
- /**
- * The Clarity token.
- */
- id: pipe(string(), minLength(10)),
-})
-```
-
-## Example
-
-Using Clarity only in production while using `clarity` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-
-::
diff --git a/docs/content/scripts/marketing/hotjar.md b/docs/content/scripts/marketing/hotjar.md
deleted file mode 100644
index a64ea5ad..00000000
--- a/docs/content/scripts/marketing/hotjar.md
+++ /dev/null
@@ -1,141 +0,0 @@
----
-title: Hotjar
-description: Use Hotjar in your Nuxt app.
-links:
-- label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/hotjar.ts
- size: xs
----
-
-[Hotjar](https://www.hotjar.com/) is a screen recorder and heatmap tool that helps you understand how users interact with your website.
-
-The simplest way to load Hotjar globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptHotjar](#useScriptHotjar) composable.
-
-## Loading Globally
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- hotjar: {
- id: 123456, // your id
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- hotjar: {
- id: 123456, // your id
- }
- }
- }
- }
-})
-```
-
-
-```ts [Environment Variables]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- hotjar: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- hotjar: {
- id: 123456, // NUXT_PUBLIC_SCRIPTS_HOTJAR_ID
- },
- },
- },
- },
-})
-```
-
-::
-
-## useScriptHotjar
-
-The `useScriptHotjar` composable lets you have fine-grain control over when and how Hotjar is loaded on your site.
-
-```ts
-const { proxy } = useScriptHotjar({
- id: 123546,
-})
-// example
-proxy.hj('identify', 123456, {
- name: 'John Doe',
- email: 'john@doe.com'
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### HotjarApi
-
-```ts
-export interface HotjarApi {
- hj: ((event: 'identify', userId: string, attributes?: Record) => void)
- & ((event: 'stateChange', path: string) => void)
- & ((event: 'event', eventName: string) => void)
- & ((event: string, arg?: string) => void)
- & ((...params: any[]) => void) & {
- q: any[]
- }
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const HotjarOptions = object({
- id: number(),
- sv: optional(number()),
-})
-```
-
-## Example
-
-Using Hotjar only in production while using `hj` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-
-::
diff --git a/docs/content/scripts/matomo-analytics.md b/docs/content/scripts/matomo-analytics.md
new file mode 100644
index 00000000..b2a7d144
--- /dev/null
+++ b/docs/content/scripts/matomo-analytics.md
@@ -0,0 +1,102 @@
+---
+
+title: Matomo Analytics
+description: Use Matomo Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/matomo-analytics.ts
+ size: xs
+
+---
+
+[Matomo Analytics](https://matomo.org/) is a great analytics solution for Nuxt Apps.
+
+It provides detailed insights into how your website is performing, how users are interacting with your content, and how they are navigating through your site.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
+
+By default, Nuxt uses a `siteId` of `1` and automatically enables page tracking via the `watch` option.
+
+```ts
+useScriptMatomoAnalytics({
+ cloudId: 'YOUR_CLOUD_ID', // e.g. nuxt.matomo.cloud
+ siteId: 2,
+ // watch: true, // enabled by default - automatic page tracking!
+})
+```
+
+If you'd like more control over the tracking, for example to set a custom dimension, you can send events using the `proxy` object.
+
+```ts
+const { proxy } = useScriptMatomoAnalytics({
+ cloudId: 'YOUR_CLOUD_ID', // e.g. nuxt.matomo.cloud
+})
+
+// set custom dimension
+proxy._paq.push(['setCustomDimension', 1, 'value'])
+// send page event
+proxy._paq.push(['trackPageView'])
+```
+
+Please see the [Config Schema](#config-schema) for all available options.
+
+## Custom Page Tracking
+
+By default, Nuxt tracks all pages automatically; provide `watch: false` to disable automatic tracking.
+
+```ts
+import { useScriptEventPage } from '#nuxt-scripts'
+
+const { proxy } = useScriptMatomoAnalytics({
+ cloudId: 'YOUR_CLOUD_ID',
+ watch: false, // disable automatic tracking
+})
+
+// Custom page tracking with additional logic
+useScriptEventPage((payload) => {
+ // Set custom dimensions based on route
+ if (payload.path.startsWith('/products')) {
+ proxy._paq.push(['setCustomDimension', 1, 'Product Page'])
+ }
+
+ // Standard Matomo tracking calls (same as built-in watch behavior)
+ proxy._paq.push(['setDocumentTitle', payload.title])
+ proxy._paq.push(['setCustomUrl', payload.path])
+ proxy._paq.push(['trackPageView'])
+
+ // Track additional custom events
+ proxy._paq.push(['trackEvent', 'Navigation', 'PageView', payload.path])
+})
+```
+
+### Using Matomo Self-Hosted
+
+For self-hosted Matomo, set `matomoUrl` to customize tracking, you may need to set the `trackerUrl` if you've customized this.
+
+```ts
+useScriptMatomoAnalytics({
+ // e.g. https://your-url.com/tracker.js & https://your-url.com//matomo.php both exists
+ matomoUrl: 'https://your-url.com',
+})
+```
+
+### Using Matomo Whitelabel
+
+For Matomo Whitelabel, set `trackerUrl` and `scriptInput.src` to customize tracking.
+
+```ts
+useScriptMatomoAnalytics({
+ trackerUrl: 'https://c.staging.cookie3.co/lake',
+ scriptInput: {
+ src: 'https://cdn.cookie3.co/scripts/latest/cookie3.analytics.min.js',
+ },
+})
+```
diff --git a/docs/content/scripts/meta-pixel.md b/docs/content/scripts/meta-pixel.md
new file mode 100644
index 00000000..64c95174
--- /dev/null
+++ b/docs/content/scripts/meta-pixel.md
@@ -0,0 +1,24 @@
+---
+
+title: Meta Pixel
+description: Use Meta Pixel in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/meta-pixel.ts
+ size: xs
+
+---
+
+[Meta Pixel](https://www.facebook.com/business/tools/meta-pixel) lets you measure, optimise and build audiences for your Facebook ad campaigns.
+
+Nuxt Scripts provides a registry script composable [`useScriptMetaPixel()`{lang="ts"}](/scripts/meta-pixel){lang="ts"} to easily integrate Meta Pixel in your Nuxt app.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/utility/npm.md b/docs/content/scripts/npm.md
similarity index 72%
rename from docs/content/scripts/utility/npm.md
rename to docs/content/scripts/npm.md
index 43dc328e..38d7c118 100644
--- a/docs/content/scripts/utility/npm.md
+++ b/docs/content/scripts/npm.md
@@ -1,4 +1,5 @@
---
+
title: NPM
description: Load IIFE scripts from NPM in your Nuxt app.
links:
@@ -6,16 +7,22 @@ links:
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/npm.ts
size: xs
+
---
+::script-stats
+::
+
+::script-types
+::
+
## Background
-When working with NPM files, you'd typically include them as a node_module dependency in the `package.json` file. However,
+When working with [npm](https://npmjs.com) files, you'd typically include them as a node_module dependency in the `package.json` file. However,
optimizing the script loading of these scripts can be difficult, requiring a dynamic import of the module from a separate chunk and
loading it only when needed. It also slows down your build as the module needs to be transpiled.
-The `useScriptNpm` registry script abstracts this process, allowing you to load scripts that have been exported as immediately invokable functions,
-with a single line of code .
+The [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"} registry script abstracts this process, allowing you to load scripts that export immediately invokable functions with a single line of code.
In many instances it will still make more sense to include the script as a dependency in the `package.json` file, but for scripts that are not used often or
are not critical to the application, this can be a great alternative.
@@ -39,7 +46,7 @@ useScript('https://cdn.jsdelivr.net/npm/js-confetti@0.12.0/dist/js-confetti.brow
```ts [useHead]
useHead({
- scripts: [
+ script: [
{ src: 'https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js' }
]
})
@@ -47,9 +54,9 @@ useHead({
::
-## useScriptNpm
+## [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"}
-The `useScriptNpm` composable lets you have fine-grain control over when and how NPM scripts are loaded on your site.
+The [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"} composable lets you have fine-grain control over when and how npm scripts load on your site.
```ts
function useScriptNpm>(_options: NpmInput) {}
@@ -57,20 +64,9 @@ function useScriptNpm>(_options: NpmInput
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-### NpmOptions
-
-```ts
-export const NpmOptions = object({
- packageName: string(),
- file: optional(string()),
- version: optional(string()),
- type: optional(string()),
-})
-```
-
### Return
-To get types for the script you're loading, you'll need to augment the types of the `useScriptNpm` function.
+To get types for the script you're loading, you'll need to augment the types of the [`useScriptNpm()`{lang="ts"}](/scripts/npm){lang="ts"} function.
```ts
interface SomeApi {
diff --git a/docs/content/scripts/payments/lemon-squeezy.md b/docs/content/scripts/payments/lemon-squeezy.md
deleted file mode 100644
index 5e45027c..00000000
--- a/docs/content/scripts/payments/lemon-squeezy.md
+++ /dev/null
@@ -1,186 +0,0 @@
----
-title: Lemon Squeezy
-description: Use Lemon Squeezy in your Nuxt app.
-links:
- - label: useScriptLemonSqueezy
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/lemon-squeezy.ts
- size: xs
- - label: ""
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptLemonSqueezy.vue
- size: xs
----
-
-[Lemon Squeezy](https://www.lemonsqueezy.com/) is a popular payment gateway that allows you to accept payments online.
-
-Nuxt Scripts provides a [useScriptLemonSqueezy](#usescriptlemonsqueezy) composable and a headless Facade Component [ScriptLemonSqueezy](#scriptlemonsqueezy) component to interact with lemon squeezy.
-
-
-## ScriptLemonSqueezy
-
-The `ScriptLemonSqueezy` component is headless [Facade Component](/docs/guides/facade-components) wrapping the [useScriptLemonSqueezy](#useScriptLemonSqueezy) composable, providing a simple, performance optimized way to load Lemon Squeezy in your Nuxt app.
-
-```vue
-
-
-
- Buy me - $9.99
-
-
-
-```
-
-It works by injecting a `.lemonsqueezy-button` class onto any `a` tags within the component then loading in
-the Lemon Squeezy script with the `visibility` [Element Event Trigger](/docs/guides/script-triggers#element-event-triggers).
-
-### Demo
-
-::code-group
-
-:lemon-squeezy-demo{label="Output"}
-
-```vue [Input]
-
-
-
-
-
- events.push(e)" @ready="ready = true">
-
- Buy me - $9.99
-
-
- Buy me - pay what you want
-
-
-
-
-
-
-
-
- Buttons are live and will open the modal, tracking events:
-
-
- {{ event.event }}
-
-
-
-
-
-
-```
-
-::
-
-### Component API
-
-See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-
-### Events
-
-***`lemon-squeezy-event`***
-
-Events emitted by the Lemon.js script are forwarded through this event. The payload is an object with an `event` key and a `data` key.
-
-```ts
-export type LemonSqueezyEventPayload = { event: 'Checkout.Success', data: Record }
- & { event: 'Checkout.ViewCart', data: Record }
- & { event: 'GA.ViewCart', data: Record }
- & { event: 'PaymentMethodUpdate.Mounted' }
- & { event: 'PaymentMethodUpdate.Closed' }
- & { event: 'PaymentMethodUpdate.Updated' }
- & { event: string }
- ```
-
-## useScriptLemonSqueezy
-
-The `useScriptLemonSqueezy` composable lets you have fine-grain control over the Lemon Squeezy SDK. It provides a way to load the Lemon Squeezy SDK and interact with it programmatically.
-
-```ts
-export function useScriptLemonSqueezy(_options?: LemonSqueezyInput) {}
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### LemonSqueezyApi
-
-```ts
-export interface LemonSqueezyApi {
- /**
- * Initialises Lemon.js on your page.
- * @param options - An object with a single property, eventHandler, which is a function that will be called when Lemon.js emits an event.
- */
- Setup: (options: {
- eventHandler: (event: { event: 'Checkout.Success', data: Record }
- & { event: 'Checkout.ViewCart', data: Record }
- & { event: 'GA.ViewCart', data: Record }
- & { event: 'PaymentMethodUpdate.Mounted' }
- & { event: 'PaymentMethodUpdate.Closed' }
- & { event: 'PaymentMethodUpdate.Updated' }
- & { event: string }
- ) => void
- }) => void
- /**
- * Refreshes `lemonsqueezy-button` listeners on the page.
- */
- Refresh: () => void
-
- Url: {
- /**
- * Opens a given Lemon Squeezy URL, typically these are Checkout or Payment Details Update overlays.
- * @param url - The URL to open.
- */
- Open: (url: string) => void
-
- /**
- * Closes the current opened Lemon Squeezy overlay checkout window.
- */
- Close: () => void
- }
- Affiliate: {
- /**
- * Retrieve the affiliate tracking ID
- */
- GetID: () => string
-
- /**
- * Append the affiliate tracking parameter to the given URL
- * @param url - The URL to append the affiliate tracking parameter to.
- */
- Build: (url: string) => string
- }
- Loader: {
- /**
- * Show the Lemon.js loader.
- */
- Show: () => void
-
- /**
- * Hide the Lemon.js loader.
- */
- Hide: () => void
- }
-}
-```
-
-## Example
-
-Using the Lemon Squeezy SDK with a payment link.
-
-```vue
-
-
-
-
-
-```
diff --git a/docs/content/scripts/paypal.md b/docs/content/scripts/paypal.md
new file mode 100644
index 00000000..7b2707f2
--- /dev/null
+++ b/docs/content/scripts/paypal.md
@@ -0,0 +1,129 @@
+---
+
+title: PayPal
+description: Use PayPal in your Nuxt app.
+links:
+ - label: useScriptPayPal
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/paypal.ts
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptPayPalButtons.vue
+ size: xs
+ - label: ""
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptPayPalMessages.vue
+ size: xs
+
+---
+
+[PayPal](https://www.paypal.com) is a popular payment gateway that allows you to accept payments online.
+
+Nuxt Scripts provides PayPal SDK v6 integration:
+- `useScriptPayPal` composable which loads the script `https://www.paypal.com/web-sdk/v6/core`.
+- `ScriptPayPalButtons` component that initializes the PayPal SDK v6 instance and exposes it via a scoped slot.
+- `ScriptPayPalMessages` component that allows you to use [PayPal Messages](https://developer.paypal.com/sdk/js/reference/) on your site.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Types
+
+To use PayPal with full TypeScript support, you will need
+to install the `@paypal/paypal-js` dependency.
+
+```bash
+pnpm add -D @paypal/paypal-js
+```
+
+The v6 types are available from `@paypal/paypal-js/sdk-v6`.
+
+### Demo
+
+::code-group
+
+:pay-pal-demo{label="Output"}
+
+```vue [Input]
+
+
+
+
+
+
+
+ Pay with PayPal
+
+
+
+
+
+```
+
+::
+
+#### With Environment Variables
+
+If you prefer to configure your client ID using environment variables.
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ paypal: true,
+ }
+ },
+ // you need to provide a runtime config to access the environment variables
+ runtimeConfig: {
+ public: {
+ scripts: {
+ paypal: {
+ clientId: '', // NUXT_PUBLIC_SCRIPTS_PAYPAL_CLIENT_ID
+ },
+ },
+ },
+ },
+})
+```
+
+```text [.env]
+NUXT_PUBLIC_SCRIPTS_PAYPAL_CLIENT_ID=
+```
diff --git a/docs/content/scripts/plausible-analytics.md b/docs/content/scripts/plausible-analytics.md
new file mode 100644
index 00000000..09771a17
--- /dev/null
+++ b/docs/content/scripts/plausible-analytics.md
@@ -0,0 +1,52 @@
+---
+
+title: Plausible Analytics
+description: Use Plausible Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/plausible-analytics.ts
+ size: xs
+
+---
+
+[Plausible Analytics](https://plausible.io/) is a privacy-friendly analytics solution for Nuxt Apps, allowing you to track your website's traffic without compromising your users' privacy.
+
+::script-stats
+::
+
+::script-docs
+::
+
+### Self-hosted Plausible
+
+If you use a self-hosted version of Plausible, provide an explicit src for the script so that the browser sends API events to the correct endpoint.
+
+```ts
+useScriptPlausibleAnalytics({
+ scriptInput: {
+ src: 'https://my-self-hosted-plausible.io/js/script.js'
+ }
+})
+```
+
+::script-types
+::
+
+**Note:** Find the `scriptId` in your Plausible dashboard under **Site Installation** in your site settings.
+
+**Extracting your Script ID:**
+
+Plausible provides you with a script tag like this:
+
+```html
+
+```
+
+Your `scriptId` is the part after `pa-` and before `.js`:
+
+```ts
+scriptId: 'gYyxvZhkMzdzXBAtSeSNz'
+// ^^^^^^^^^^^^^^^^^^^^^^^
+// Extract from: pa-{scriptId}.js
+```
diff --git a/docs/content/scripts/posthog.md b/docs/content/scripts/posthog.md
new file mode 100644
index 00000000..9787cc14
--- /dev/null
+++ b/docs/content/scripts/posthog.md
@@ -0,0 +1,123 @@
+---
+
+title: PostHog
+description: Use PostHog in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/posthog.ts
+ size: xs
+
+---
+
+[PostHog](https://posthog.com) is an open-source product analytics platform that provides analytics, session replay, feature flags, A/B testing, and more.
+
+Nuxt Scripts provides a registry script composable [`useScriptPostHog()`{lang="ts"}](/scripts/posthog){lang="ts"} to easily integrate PostHog in your Nuxt app.
+
+## Installation
+
+You must install the `posthog-js` dependency:
+
+```bash
+pnpm add posthog-js
+```
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
+
+## EU Hosting
+
+To use PostHog's EU cloud:
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ posthog: {
+ apiKey: 'YOUR_API_KEY',
+ region: 'eu'
+ }
+ }
+ }
+})
+```
+
+## First-Party Proxy
+
+When you enable [first-party mode](/docs/guides/first-party), your server automatically proxies PostHog requests. This improves event capture reliability by avoiding ad blockers. Nuxt applies no privacy anonymization - PostHog is a trusted, open-source tool that requires full-fidelity data for GeoIP enrichment, feature flags, and session replay.
+
+You don't need additional configuration - the module automatically sets `apiHost` to route through your server's proxy endpoint:
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: true, // enabled by default
+ registry: {
+ posthog: {
+ apiKey: 'YOUR_API_KEY',
+ // apiHost is auto-set to '/_proxy/ph' (or '/_proxy/ph-eu' for EU region)
+ }
+ }
+ }
+})
+```
+
+The proxy handles both API requests and static assets (e.g. session recording SDK), routing them to the correct PostHog endpoints.
+
+## Custom API Host
+
+To use a custom reverse proxy or self-hosted PostHog instance, set `apiHost` directly:
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ posthog: {
+ apiKey: 'YOUR_API_KEY',
+ apiHost: '/my-proxy'
+ }
+ }
+ }
+})
+```
+
+The `apiHost` option accepts any URL or relative path, overriding both the `region` default and the first-party proxy auto-configuration. For additional PostHog SDK options like `ui_host`, use the `config` passthrough.
+
+## Feature Flags
+
+Feature flag methods return values, so you need to wait for PostHog to load first:
+
+```ts
+const { onLoaded } = useScriptPostHog()
+
+onLoaded(({ posthog }) => {
+ // Check a feature flag
+ if (posthog.isFeatureEnabled('new-dashboard')) {
+ // Show new dashboard
+ }
+
+ // Get flag payload
+ const payload = posthog.getFeatureFlagPayload('experiment-config')
+})
+```
+
+## Disabling Session Recording
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ posthog: {
+ apiKey: 'YOUR_API_KEY',
+ disableSessionRecording: true
+ }
+ }
+ }
+})
+```
diff --git a/docs/content/scripts/reddit-pixel.md b/docs/content/scripts/reddit-pixel.md
new file mode 100644
index 00000000..8794126c
--- /dev/null
+++ b/docs/content/scripts/reddit-pixel.md
@@ -0,0 +1,24 @@
+---
+
+title: Reddit Pixel
+description: Use Reddit Pixel in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/reddit-pixel.ts
+ size: xs
+
+---
+
+[Reddit Pixel](https://advertising.reddithelp.com/en/categories/custom-audiences-and-conversion-tracking/reddit-pixel) helps you track conversions and build audiences for your Reddit advertising campaigns.
+
+Nuxt Scripts provides a registry script composable [`useScriptRedditPixel()`{lang="ts"}](/scripts/reddit-pixel){lang="ts"} to easily integrate Reddit Pixel in your Nuxt app.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/rybbit-analytics.md b/docs/content/scripts/rybbit-analytics.md
new file mode 100644
index 00000000..7c6a0f24
--- /dev/null
+++ b/docs/content/scripts/rybbit-analytics.md
@@ -0,0 +1,50 @@
+---
+
+title: Rybbit Analytics
+description: Use Rybbit Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/rybbit.ts
+ size: xs
+
+---
+
+[Rybbit Analytics](https://www.rybbit.io/) is a privacy-focused analytics solution for tracking user activity on your website without compromising your users' privacy.
+
+::script-stats
+::
+
+::script-docs
+::
+
+### Self-hosted Rybbit Analytics
+
+If you are using a self-hosted version of Rybbit Analytics, you can provide a custom script source:
+
+```ts
+useScriptRybbitAnalytics({
+ scriptInput: {
+ src: 'https://your-rybbit-instance.com/api/script.js'
+ },
+ siteId: 'YOUR_SITE_ID'
+})
+```
+
+::script-types
+::
+
+#### Configuration Options
+
+- `siteId` (required): Your Rybbit Analytics site ID
+`autoTrackPageview`: Set to `false` to disable automatic tracking of the initial pageview when the script loads. You will need to manually call the pageview function to track pageviews. Default: `true`
+- `trackSpa`: Set to `false` to disable automatic pageview tracking for single page applications
+- `trackQuery`: Set to `false` to disable tracking of URL query strings
+- `trackOutbound`: Set to `false` to disable automatic tracking of outbound link clicks. Default: `true`
+- `trackErrors`: Set to `true` to enable automatic tracking of JavaScript errors and unhandled promise rejections. Only tracks errors from the same origin to avoid noise from third-party scripts. Default: `false`
+- `sessionReplay`: Set to `true` to enable session replay recording. Captures user interactions, mouse movements, and DOM changes for debugging and user experience analysis. Default: `false`
+- `webVitals`: Set to `true` to enable Web Vitals performance metrics collection (LCP, CLS, INP, FCP, TTFB). Nuxt disables Web Vitals by default to reduce script size and network requests. Default: `false`
+- `skipPatterns`: Array of URL path patterns to ignore
+- `maskPatterns`: Array of URL path patterns to mask for privacy
+- `debounce`: Delay in milliseconds before tracking a pageview after URL changes
+- `apiKey`: API key for tracking from localhost during development. Bypasses origin validation for self-hosted Rybbit Analytics instances
diff --git a/docs/content/scripts/segment.md b/docs/content/scripts/segment.md
new file mode 100644
index 00000000..2487279f
--- /dev/null
+++ b/docs/content/scripts/segment.md
@@ -0,0 +1,24 @@
+---
+
+title: Segment
+description: Use Segment in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/segment.ts
+ size: xs
+
+---
+
+[Segment](https://segment.com/) lets you collect, clean, and control your customer data. Segment helps you to understand your customers and personalize their experience.
+
+Nuxt Scripts provides a registry script composable [`useScriptSegment()`{lang="ts"}](/scripts/segment){lang="ts"} to easily integrate Segment in your Nuxt app.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/snapchat-pixel.md b/docs/content/scripts/snapchat-pixel.md
new file mode 100644
index 00000000..afbb6d01
--- /dev/null
+++ b/docs/content/scripts/snapchat-pixel.md
@@ -0,0 +1,24 @@
+---
+
+title: Snapchat Pixel
+description: Use Snapchat Pixel in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/snapchat-pixel.ts
+ size: xs
+
+---
+
+[Snapchat Pixel](https://businesshelp.snapchat.com/s/article/snap-pixel-about){:target="_blank"} lets you measure the crossdevice impact for your Snapchat ad campaigns.
+
+Nuxt Scripts provides a registry script composable [`useScriptSnapchatPixel()`{lang="ts"}](/scripts/snapchat-pixel){lang="ts"} to easily integrate Snapchat Pixel in your Nuxt app.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/payments/stripe.md b/docs/content/scripts/stripe.md
similarity index 67%
rename from docs/content/scripts/payments/stripe.md
rename to docs/content/scripts/stripe.md
index 6b52fcaf..5f1a1b82 100644
--- a/docs/content/scripts/payments/stripe.md
+++ b/docs/content/scripts/stripe.md
@@ -1,4 +1,5 @@
---
+
title: Stripe
description: Use Stripe in your Nuxt app.
links:
@@ -10,13 +11,60 @@ links:
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptStripePricingTable.vue
size: xs
+
---
[Stripe](https://stripe.com) is a popular payment gateway that allows you to accept payments online.
Nuxt Scripts provides two Stripe features:
-- `useScriptStripe` composable which gives loads in the Stripe SDK and provides a way to interact with it programmatically.
-- `ScriptStripePricingTable` component that allows you to embed a [Stripe Pricing Table](https://docs.stripe.com/payments/checkout/pricing-table) on your site.
+- [`useScriptStripe()`{lang="ts"}](/scripts/stripe){lang="ts"} composable which loads the script `https://js.stripe.com/v3/`.
+- `ScriptStripePricingTable` component that allows you to embed a [Stripe Pricing Table](https://docs.stripe.com/payments/checkout/pricing-table) on your site using `https://js.stripe.com/v3/pricing-table.js`.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Types
+
+To use the Stripe with full TypeScript support, you will need
+to install the `@stripe/stripe-js` dependency.
+
+```bash
+pnpm add -D @stripe/stripe-js
+```
+
+## Loading Globally
+
+Stripe recommends loading their script globally on your app to improve fraud detection.
+
+::code-group
+
+```ts [Always enabled]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ stripe: true,
+ }
+ }
+})
+```
+
+```ts [Production only]
+export default defineNuxtConfig({
+ $production: {
+ scripts: {
+ registry: {
+ stripe: true,
+ }
+ }
+ }
+})
+```
+
+::
+
## ScriptStripePricingTable
@@ -50,21 +98,9 @@ You'll need to create your own [Pricing Table](https://dashboard.stripe.com/pric
See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-### Props
-
-The `ScriptStripePricingTable` component accepts the following props:
-
-- `trigger`: The trigger event to load the Stripe. Default is `mouseover`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
-- `pricing-table-id`: The ID of the Pricing Table you created in the Stripe Dashboard.
-- `publishable-key`: Your Stripe publishable key.
-- `client-reference-id`: A unique identifier for the client.
-- `customer-email`: The email of the customer.
-- `customer-session-client-secret`: The client secret of the customer session.
-
-
-## useScriptStripe
+## [`useScriptStripe()`{lang="ts"}](/scripts/stripe){lang="ts"}
-The `useScriptStripe` composable lets you have fine-grain control over the Stripe SDK. It provides a way to load the Stripe SDK and interact with it programmatically.
+The [`useScriptStripe()`{lang="ts"}](/scripts/stripe){lang="ts"} composable lets you have fine-grain control over the Stripe SDK. It provides a way to load the Stripe SDK and interact with it programmatically.
```ts
export function useScriptStripe(_options?: StripeInput) {}
@@ -72,22 +108,6 @@ export function useScriptStripe(_options?: StripeInput) {}
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-### Options
-
-```ts
-export const StripeOptions = object({
- advancedFraudSignals: optional(boolean()),
-})
-```
-
-### StripeApi
-
-```ts
-export interface StripeApi {
- Stripe: stripe.StripeStatic
-}
-```
-
## Example
Loading the Stripe SDK and using it to create a payment element.
diff --git a/docs/content/scripts/support/crisp.md b/docs/content/scripts/support/crisp.md
deleted file mode 100644
index 8e2ce541..00000000
--- a/docs/content/scripts/support/crisp.md
+++ /dev/null
@@ -1,243 +0,0 @@
----
-title: Crisp
-description: Show performance-optimized Crisp in your Nuxt app.
-links:
- - label: useScriptCrisp
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/crisp.ts
- size: xs
- - label: ""
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptCrisp.vue
- size: xs
----
-
-[Crisp](https://crisp.chat/) is a customer messaging platform that lets you communicate with your customers through chat, email, and more.
-
-Nuxt Scripts provides a [useScriptCrisp](#usescriptcrisp) composable and a headless Facade Component [ScriptCrisp](#scriptcrisp) component to interact with crisp.
-
-## ScriptCrisp
-
-The `ScriptCrisp` component is headless Facade Component wrapping the [useScriptCrisp](#usescriptcrisp) composable, providing a simple, performance optimized way to load Crisp in your Nuxt app.
-
-It's optimized for performance by leveraging the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading crisp when specific elements events happen.
-
-By default, it will load on the `click` DOM event.
-
-### Demo
-
-::code-group
-
-:crisp-demo{label="Output"}
-
-```vue [Input]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-::
-
-### Component API
-
-See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-
-### Props
-
-- `trigger`: The trigger event to load crisp. Default is `click`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
-- `id`: Crisp ID.
-- `runtimeConfig`: Extra configuration options. Used to configure the locale. Same as CRISP_RUNTIME_CONFIG.
-- `tokenId`: Associated a session, equivalent to using CRISP_TOKEN_ID variable. Same as CRISP_TOKEN_ID.
-- `cookieDomain`: Restrict the domain that crisp cookie is set on. Same as CRISP_COOKIE_DOMAIN.
-- `cookieExpiry`: The cookie expiry in seconds. Same as CRISP_COOKIE_EXPIRATION.
-
-See the [Config Schema](#config-schema) for full details.
-
-### Events
-
-The `ScriptCrisp` component emits a single `ready` event when crisp is loaded.
-
-```ts
-const emits = defineEmits<{
- ready: [crisp: Crisp]
-}>()
-```
-
-```vue
-
-
-
-
-
-```
-
-### Slots
-
-**awaitingLoad**
-
-The slot is used to display content while crisp is loading.
-
-```vue
-
-
-
-
- chat!
-
-
-
-
-```
-
-**loading**
-
-The slot is used to display content while crisp is loading.
-
-Tip: You should use the `ScriptLoadingIndicator` by default for accessibility and UX.
-
-```vue
-
-
-
-
- Loading...
-
-
-
-
-```
-
-## useScriptCrisp
-
-The `useScriptCrisp` composable lets you have fine-grain control over Crisp SDK. It provides a way to load crisp SDK and interact with it programmatically.
-
-```ts
-export function useScriptCrisp(_options?: CrispInput) {}
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### Config Schema
-
-```ts
-export const CrispOptions = object({
- /**
- * Crisp ID.
- */
- id: string(),
- /**
- * Extra configuration options. Used to configure the locale.
- * Same as CRISP_RUNTIME_CONFIG.
- * @see https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/language-customization/
- */
- runtimeConfig: optional(object({
- locale: optional(string()),
- })),
- /**
- * Associated a session, equivalent to using CRISP_TOKEN_ID variable.
- * Same as CRISP_TOKEN_ID.
- * @see https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/session-continuity/
- */
- tokenId: optional(string()),
- /**
- * Restrict the domain that crisp cookie is set on.
- * Same as CRISP_COOKIE_DOMAIN.
- * @see https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/cookie-policies/
- */
- cookieDomain: optional(string()),
- /**
- * The cookie expiry in seconds.
- * Same as CRISP_COOKIE_EXPIRATION.
- * @see https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/cookie-policies/#change-cookie-expiration-date
- */
- cookieExpiry: optional(number()),
-})
-```
-
-### CrispApi
-
-```ts
-export interface CrispApi {
- push: (...args: any[]) => void
- is: (name: 'chat:opened' | 'chat:closed' | 'chat:visible' | 'chat:hidden' | 'chat:small' | 'chat:large' | 'session:ongoing' | 'website:available' | 'overlay:opened' | 'overlay:closed' | string) => boolean
- set: (name: 'message:text' | 'session:data' | 'session:segments' | 'session:event' | 'user:email' | 'user:phone' | 'user:nickname' | 'user:avatar' | 'user:company' | string, value: any) => void
- get: (name: 'chat:unread:count' | 'message:text' | 'session:identifier' | 'session:data' | 'user:email' | 'user:phone' | 'user:nickname' | 'user:avatar' | 'user:company' | string) => any
- do: (name: 'chat:open' | 'chat:close' | 'chat:toggle' | 'chat:show' | 'chat:hide' | 'helpdesk:search' | 'helpdesk:article:open' | 'helpdesk:query' | 'overlay:open' | 'overlay:close' | 'message:send' | 'message:show' | 'message:read' | 'message:thread:start' | 'message:thread:end' | 'session:reset' | 'trigger:run' | string, arg2?: any) => any
- on: (name: 'session:loaded' | 'chat:initiated' | 'chat:opened' | 'chat:closed' | 'message:sent' | 'message:received' | 'message:compose:sent' | 'message:compose:received' | 'user:email:changed' | 'user:phone:changed' | 'user:nickname:changed' | 'user:avatar:changed' | 'website:availability:changed' | 'helpdesk:queried' | string, callback: (...args: any[]) => any) => void
- off: (name: 'session:loaded' | 'chat:initiated' | 'chat:opened' | 'chat:closed' | 'message:sent' | 'message:received' | 'message:compose:sent' | 'message:compose:received' | 'user:email:changed' | 'user:phone:changed' | 'user:nickname:changed' | 'user:avatar:changed' | 'website:availability:changed' | 'helpdesk:queried' | string, callback: (...args: any[]) => any) => void
- config: (options: any) => void
- help: () => void
- [key: string]: any
-}
-```
-
-For more information, please refer to the [Crisp API documentation](https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/dollar-crisp/).
-
-## Example
-
-Loading the Crisp SDK and interacting with it programmatically.
-
-```vue
-
-```
diff --git a/docs/content/scripts/support/intercom.md b/docs/content/scripts/support/intercom.md
deleted file mode 100644
index e60d9a6d..00000000
--- a/docs/content/scripts/support/intercom.md
+++ /dev/null
@@ -1,260 +0,0 @@
----
-title: Intercom
-description: Use Intercom in your Nuxt app.
-links:
- - label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/intercom.ts
- size: xs
- - label: ""
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptIntercom.vue
- size: xs
----
-
-[Intercom](https://www.intercom.com/) is a customer messaging platform that helps you build better customer relationships.
-
-Nuxt Scripts provides a [useScriptIntercom](#usescriptintercom) composable and a headless Facade Component [ScriptIntercom](#scriptintercom) component to interact with Intercom.
-
-## ScriptIntercom
-
-
-The `ScriptIntercom` component is headless Facade Component wrapping the [useScriptIntercom](#usescriptintercom) composable, providing a simple, performance optimized way to load Intercom in your Nuxt app.
-
-It's optimized for performance by leveraging the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading Intercom when specific elements events happen.
-
-By default, it will load on the `click` DOM event.
-
-### Demo
-
-::code-group
-
-:intercom-demo{label="Output"}
-
-```vue [Input]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-::
-
-### Component API
-
-See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
-
-### Props
-
-- `trigger`: The trigger event to load intercom. Default is `click`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
-- `app-id`: The Intercom app id.
-- `api-base`: The Intercom API base URL.
-- `name`: The name of the user.
-- `email`: The email of the user.
-- `user-id`: The user id.
-- `alignment`: The alignment of the messenger `left` or `right`. Default is `right`.
-- `horizontal-padding`: The horizontal padding of the messenger. Default is `20`.
-- `vertical-padding`: The vertical padding of the messenger. Default is `20`.
-
-See the [Config Schema](#config-schema) for full details.
-
-### Events
-
-The `ScriptIntercom` component emits a single `ready` event when Intercom is loaded.
-
-```ts
-const emits = defineEmits<{
- ready: [intercom: Intercom]
-}>()
-```
-
-```vue
-
-
-
-
-
-```
-
-### Intercom API
-
-The component exposes a `intercom` instance that you can access the underlying Intercom API.
-
-```vue
-
-
-
-
-
-```
-
-### Slots
-
-The component provides minimal UI by default, only enough to be functional and accessible. There are a number of slots for you to customize the maps however you like.
-
-**default**
-
-The default slot is used to display content that will always be visible.
-
-**awaitingLoad**
-
-The slot is used to display content while Intercom is not loading.
-
-```vue
-
-
-
-
- chat!
-
-
-
-
-```
-
-**loading**
-
-The slot is used to display content while Intercom is loading.
-
-Tip: You should use the `ScriptLoadingIndicator` by default for accessibility and UX.
-
-```vue
-
-
-
-
-
-```
-
-
-::
diff --git a/docs/content/scripts/tiktok-pixel.md b/docs/content/scripts/tiktok-pixel.md
new file mode 100644
index 00000000..1313aaae
--- /dev/null
+++ b/docs/content/scripts/tiktok-pixel.md
@@ -0,0 +1,54 @@
+---
+
+title: TikTok Pixel
+description: Use TikTok Pixel in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/tiktok-pixel.ts
+ size: xs
+
+---
+
+[TikTok Pixel](https://ads.tiktok.com/help/article/tiktok-pixel) lets you measure, optimize and build audiences for your TikTok ad campaigns.
+
+Nuxt Scripts provides a registry script composable [`useScriptTikTokPixel()`{lang="ts"}](/scripts/tiktok-pixel){lang="ts"} to easily integrate TikTok Pixel in your Nuxt app.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
+
+## Identifying Users
+
+You can identify users for advanced matching:
+
+```ts
+const { proxy } = useScriptTikTokPixel()
+
+proxy.ttq('identify', {
+ email: 'user@example.com',
+ phone_number: '+1234567890'
+})
+```
+
+## Disabling Auto Page View
+
+By default, TikTok Pixel tracks page views automatically. To disable:
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ tiktokPixel: {
+ id: 'YOUR_PIXEL_ID',
+ trackPageView: false
+ }
+ }
+ }
+})
+```
diff --git a/docs/content/scripts/tracking/google-tag-manager.md b/docs/content/scripts/tracking/google-tag-manager.md
deleted file mode 100644
index 3c21cb0b..00000000
--- a/docs/content/scripts/tracking/google-tag-manager.md
+++ /dev/null
@@ -1,158 +0,0 @@
----
-title: Google Tag Manager
-description: Use Google Tag Manager in your Nuxt app.
-links:
-- label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-tag-manager.ts
- size: xs
----
-
-::tip
-This composable is generated with [GoogleChromeLabs/third-party-capital](https://github.com/GoogleChromeLabs/third-party-capital) in collaboration with the [Chrome Aurora team](https://developer.chrome.com/docs/aurora).
-::
-
-[Google Tag Manager](https://marketingplatform.google.com/about/tag-manager/) is a tag management system that allows you to quickly and easily update tags and code snippets on your website or mobile app, such as those intended for traffic analysis and marketing optimization.
-
-::callout
-You may not need Google Tag Manager with Nuxt Scripts. GTM is 82kb and will slow down your site.
-Nuxt Scripts provides many features you can easily
-implement within your Nuxt app. If you're using GTM for Google Tag Manager, you can use the `useScriptGoogleAnalytics` composable instead.
-::
-
-### Nuxt Config Setup
-
-The simplest way to load Google Tag Manager globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptGoogleTagManager](#useScriptGoogleTagManager) composable.
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- googleTagManager: {
- id: 'YOUR_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- googleTagManager: {
- token: 'YOUR_TOKEN_ID',
- }
- }
- }
- }
-})
-```
-
-::
-
-#### With Environment Variables
-
-If you prefer to configure your id using environment variables.
-
-```ts [nuxt.config.ts]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- googleTagManager: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- googleTagManager: {
- id: '', // NUXT_PUBLIC_SCRIPTS_GOOGLE_TAG_MANAGER_ID
- },
- },
- },
- },
-})
-```
-
-```text [.env]
-NUXT_PUBLIC_SCRIPTS_GOOGLE_TAG_MANAGER_ID=
-```
-
-## useScriptGoogleTagManager
-
-The `useScriptGoogleTagManager` composable lets you have fine-grain control over when and how Google Tag Manager is loaded on your site.
-
-```ts
-const { proxy } = useScriptGoogleTagManager({
- id: 'YOUR_ID'
-})
-// example
-proxy.dataLayer.push({ event: 'conversion', value: 1 })
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### GoogleTagManagerApi
-
-```ts
-interface GoogleTagManagerApi {
- dataLayer: Record[]
- google_tag_manager: GoogleTagManager
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const GoogleTagManagerOptions = object({
- /**
- * The Google Tag Manager ID.
- */
- id: string(),
- /**
- * The name of the dataLayer you want to use
- * @default 'defaultGtm'
- */
- dataLayerName: optional(string())
-})
-```
-
-## Example
-
-Using Google Tag Manager only in production while using `dataLayer` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-
-::
diff --git a/docs/content/scripts/tracking/meta-pixel.md b/docs/content/scripts/tracking/meta-pixel.md
deleted file mode 100644
index aec8e6b7..00000000
--- a/docs/content/scripts/tracking/meta-pixel.md
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: Meta Pixel
-description: Use Meta Pixel in your Nuxt app.
-links:
-- label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/meta-pixel.ts
- size: xs
----
-
-[Meta Pixel](https://www.facebook.com/business/tools/meta-pixel) lets you measure, optimise and build audiences for your Facebook ad campaigns.
-
-Nuxt Scripts provides a registry script composable `useScriptMetaPixel` to easily integrate Meta Pixel in your Nuxt app.
-
-### Nuxt Config Setup
-
-The simplest way to load Meta Pixel globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptMetaPixel](#useScriptMetaPixel) composable.
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- metaPixel: {
- id: 'YOUR_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- metaPixel: {
- id: 'YOUR_ID',
- }
- }
- }
- }
-})
-```
-
-::
-
-#### With Environment Variables
-
-If you prefer to configure your id using environment variables.
-
-```ts [nuxt.config.ts]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- metaPixel: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- metaPixel: {
- id: '', // NUXT_PUBLIC_SCRIPTS_META_PIXEL_ID
- },
- },
- },
- },
-})
-```
-
-```text [.env]
-NUXT_PUBLIC_SCRIPTS_META_PIXEL_ID=
-```
-
-## useScriptMetaPixel
-
-The `useScriptMetaPixel` composable lets you have fine-grain control over when and how Meta Pixel is loaded on your site.
-
-```ts
-const { proxy } = useScriptMetaPixel({
- id: 'YOUR_ID'
-})
-// example
-proxy.fbq('track', 'ViewContent', {
- content_name: 'Nuxt Pixel',
- content_category: 'Nuxt',
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### MetaPixelApi
-
-```ts
-export interface MetaPixelApi {
- fbq: FbqFns & {
- push: FbqFns
- loaded: boolean
- version: string
- queue: any[]
- }
- _fbq: MetaPixelApi['fbq']
-}
-type FbqFns = ((event: 'track', eventName: StandardEvents, data?: EventObjectProperties) => void)
- & ((event: 'trackCustom', eventName: string, data?: EventObjectProperties) => void)
- & ((event: 'init', id: number, data?: Record) => void)
- & ((event: 'init', id: string) => void)
- & ((event: string, ...params: any[]) => void)
-type StandardEvents = 'AddPaymentInfo' | 'AddToCart' | 'AddToWishlist' | 'CompleteRegistration' | 'Contact' | 'CustomizeProduct' | 'Donate' | 'FindLocation' | 'InitiateCheckout' | 'Lead' | 'Purchase' | 'Schedule' | 'Search' | 'StartTrial' | 'SubmitApplication' | 'Subscribe' | 'ViewContent'
-interface EventObjectProperties {
- content_category?: string
- content_ids?: string[]
- content_name?: string
- content_type?: string
- contents: { id: string, quantity: number }[]
- currency?: string
- delivery_category?: 'in_store' | 'curbside' | 'home_delivery'
- num_items?: number
- predicted_ltv?: number
- search_string?: string
- status?: 'completed' | 'updated' | 'viewed' | 'added_to_cart' | 'removed_from_cart' | string
- value?: number
- [key: string]: any
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const MetaPixelOptions = object({
- id: number(),
- sv: optional(number()),
-})
-```
-
-## Example
-
-Using Meta Pixel only in production while using `fbq` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-::
diff --git a/docs/content/scripts/tracking/segment.md b/docs/content/scripts/tracking/segment.md
deleted file mode 100644
index daa0aaff..00000000
--- a/docs/content/scripts/tracking/segment.md
+++ /dev/null
@@ -1,150 +0,0 @@
----
-title: Segment
-description: Use Segment in your Nuxt app.
-links:
-- label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/segment.ts
- size: xs
----
-
-[Segment](https://segment.com/) lets you collect, clean, and control your customer data. Segment helps you to understand your customers and personalize their experience.
-
-Nuxt Scripts provides a registry script composable `useScriptSegment` to easily integrate Segment in your Nuxt app.
-
-### Nuxt Config Setup
-
-The simplest way to load Segment globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptSegment](#useScriptSegment) composable.
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- segment: {
- writeKey: 'YOUR_WRITE_KEY'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- segment: {
- writeKey: 'YOUR_WRITE_KEY'
- }
- }
- }
- }
-})
-```
-
-::
-
-#### With Environment Variables
-
-If you prefer to configure your id using environment variables.
-
-```ts [nuxt.config.ts]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- segment: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- segment: {
- writeKey: '' // NUXT_PUBLIC_SCRIPTS_SEGMENT_WRITE_KEY
- }
- },
- },
- },
-})
-```
-
-```text [.env]
-NUXT_PUBLIC_SCRIPTS_SEGMENT_WRITE_KEY=
-```
-
-## useScriptSegment
-
-The `useScriptSegment` composable lets you have fine-grain control over when and how Segment is loaded on your site.
-
-```ts
-const { proxy } = useScriptSegment({
- id: 'YOUR_ID'
-})
-// example
-proxy.track('event', {
- foo: 'bar'
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### SegmentApi
-
-```ts
-interface SegmentApi {
- track: (event: string, properties?: Record) => void
- page: (name?: string, properties?: Record) => void
- identify: (userId: string, traits?: Record, options?: Record) => void
- group: (groupId: string, traits?: Record, options?: Record) => void
- alias: (userId: string, previousId: string, options?: Record) => void
- reset: () => void
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const SegmentOptions = object({
- writeKey: string(),
- analyticsKey: optional(string()),
-})
-```
-
-## Example
-
-Using Segment only in production while using `analytics` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-::
diff --git a/docs/content/scripts/tracking/x-pixel.md b/docs/content/scripts/tracking/x-pixel.md
deleted file mode 100644
index 010cc838..00000000
--- a/docs/content/scripts/tracking/x-pixel.md
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: X Pixel
-description: Use X Pixel in your Nuxt app.
-links:
-- label: Source
- icon: i-simple-icons-github
- to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/x-pixel.ts
- size: xs
----
-
-[X Pixel](https://x.com/) lets you collect, clean, and control your customer data. X helps you to understand your customers and personalize their experience.
-
-Nuxt Scripts provides a registry script composable `useScriptXPixel` to easily integrate X Pixel in your Nuxt app.
-
-### Nuxt Config Setup
-
-The simplest way to load Meta Pixel globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
-use the [useScriptXPixel](#useScriptXPixel) composable.
-
-If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to
-disable the script in development.
-
-::code-group
-
-```ts [Always enabled]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- xPixel: {
- id: 'YOUR_ID'
- }
- }
- }
-})
-```
-
-```ts [Production only]
-export default defineNuxtConfig({
- $production: {
- scripts: {
- registry: {
- xPixel: {
- id: 'YOUR_ID',
- }
- }
- }
- }
-})
-```
-
-::
-
-#### With Environment Variables
-
-If you prefer to configure your id using environment variables.
-
-```ts [nuxt.config.ts]
-export default defineNuxtConfig({
- scripts: {
- registry: {
- xPixel: true,
- }
- },
- // you need to provide a runtime config to access the environment variables
- runtimeConfig: {
- public: {
- scripts: {
- xPixel: {
- id: '', // NUXT_PUBLIC_SCRIPTS_X_PIXEL_ID
- },
- },
- },
- },
-})
-```
-
-```text [.env]
-NUXT_PUBLIC_SCRIPTS_X_PIXEL_ID=
-```
-
-## useScriptXPixel
-
-The `useScriptXPixel` composable lets you have fine-grain control over when and how X Pixel is loaded on your site.
-
-```ts
-const { proxy } = useScriptXPixel({
- id: 'YOUR_ID'
-})
-// example
-proxy.twq('event', '', {
- value: 1,
-})
-```
-
-Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-
-### XPixelApi
-
-```ts
-export interface XPixelApi {
- fbq: FbqFns & {
- push: FbqFns
- loaded: boolean
- version: string
- queue: any[]
- }
- _fbq: XPixelApi['fbq']
-}
-type FbqFns = ((event: 'track', eventName: StandardEvents, data?: EventObjectProperties) => void)
- & ((event: 'trackCustom', eventName: string, data?: EventObjectProperties) => void)
- & ((event: 'init', id: number, data?: Record) => void)
- & ((event: 'init', id: string) => void)
- & ((event: string, ...params: any[]) => void)
-type StandardEvents = 'AddPaymentInfo' | 'AddToCart' | 'AddToWishlist' | 'CompleteRegistration' | 'Contact' | 'CustomizeProduct' | 'Donate' | 'FindLocation' | 'InitiateCheckout' | 'Lead' | 'Purchase' | 'Schedule' | 'Search' | 'StartTrial' | 'SubmitApplication' | 'Subscribe' | 'ViewContent'
-interface EventObjectProperties {
- content_category?: string
- content_ids?: string[]
- content_name?: string
- content_type?: string
- contents: { id: string, quantity: number }[]
- currency?: string
- delivery_category?: 'in_store' | 'curbside' | 'home_delivery'
- num_items?: number
- predicted_ltv?: number
- search_string?: string
- status?: 'completed' | 'updated' | 'viewed' | 'added_to_cart' | 'removed_from_cart' | string
- value?: number
- [key: string]: any
-}
-```
-
-### Config Schema
-
-You must provide the options when setting up the script for the first time.
-
-```ts
-export const XPixelOptions = object({
- id: string(),
- version: optional(string()),
-})
-```
-
-## Example
-
-Using X Pixel only in production while using `twq` to send a conversion event.
-
-::code-group
-
-```vue [ConversionButton.vue]
-
-
-
-
-
- Send Conversion
-
-
-
-```
-
-
-::
diff --git a/docs/content/scripts/umami-analytics.md b/docs/content/scripts/umami-analytics.md
new file mode 100644
index 00000000..ffcde813
--- /dev/null
+++ b/docs/content/scripts/umami-analytics.md
@@ -0,0 +1,94 @@
+---
+
+title: Umami Analytics
+description: Use Umami Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/umami-analytics.ts
+ size: xs
+
+---
+
+[Umami](https://umami.is/) collects all the metrics you care about to help you make better decisions.
+
+::script-stats
+::
+
+::script-docs
+::
+
+### Self-hosted Umami
+
+If you use a self-hosted version of Umami, provide an explicit src for the script so that the browser sends API events to the correct endpoint.
+
+```ts
+useScriptUmamiAnalytics({
+ scriptInput: {
+ src: 'https://my-self-hosted/script.js'
+ }
+})
+```
+
+::script-types
+::
+
+## Advanced Features
+
+### Session Identification
+
+Umami v2.18.0+ supports setting unique session IDs using the `identify` function. You can pass either a string (unique ID) or an object with session data:
+
+```ts
+const { proxy } = useScriptUmamiAnalytics({
+ websiteId: 'YOUR_WEBSITE_ID'
+})
+
+// Using a unique string ID
+proxy.identify('user-12345')
+
+// Using session data object
+proxy.identify({
+ userId: 'user-12345',
+ plan: 'premium'
+})
+```
+
+### Data Filtering with beforeSend
+
+The `beforeSend` option allows you to inspect, modify, or cancel data before it's sent to Umami. This is useful for implementing custom privacy controls or data filtering:
+
+```ts
+useScriptUmamiAnalytics({
+ websiteId: 'YOUR_WEBSITE_ID',
+ beforeSend: (type, payload) => {
+ // Log what's being sent (for debugging)
+ console.log('Sending to Umami:', type, payload)
+
+ // Filter out sensitive data
+ if (payload.url && payload.url.includes('private')) {
+ return false // Cancel send
+ }
+
+ // Modify payload before sending
+ return {
+ ...payload,
+ referrer: '' // Remove referrer for privacy
+ }
+ }
+})
+```
+
+You can also provide a string with the name of a globally defined function:
+
+```ts
+// Define function globally
+window.myBeforeSendHandler = (type, payload) => {
+ return checkPrivacyRules(payload) ? payload : false
+}
+
+useScriptUmamiAnalytics({
+ websiteId: 'YOUR_WEBSITE_ID',
+ beforeSend: 'myBeforeSendHandler'
+})
+```
diff --git a/docs/content/scripts/vercel-analytics.md b/docs/content/scripts/vercel-analytics.md
new file mode 100644
index 00000000..c2d87210
--- /dev/null
+++ b/docs/content/scripts/vercel-analytics.md
@@ -0,0 +1,121 @@
+---
+
+title: Vercel Analytics
+description: Use Vercel Analytics in your Nuxt app.
+links:
+ - label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/vercel-analytics.ts
+ size: xs
+
+---
+
+[Vercel Analytics](https://vercel.com/docs/analytics) provides lightweight, privacy-friendly web analytics for your Nuxt app. It tracks page views and custom events with zero configuration when deployed on [Vercel](https://vercel.com).
+
+::script-stats
+::
+
+::script-docs
+::
+
+### Non-Vercel Deployment
+
+When deploying outside of Vercel, provide your DSN explicitly:
+
+```ts
+useScriptVercelAnalytics({
+ dsn: 'YOUR_DSN',
+})
+```
+
+### First-Party Mode
+
+When you enable `scripts.firstParty`, Nuxt bundles the analytics script locally and proxies data collection requests through your server. This prevents ad blockers from blocking analytics and removes sensitive data from third-party requests.
+
+```ts
+export default defineNuxtConfig({
+ scripts: {
+ firstParty: true,
+ registry: {
+ vercelAnalytics: true,
+ }
+ }
+})
+```
+
+## Defaults
+
+- **Trigger: Client** Script will load when Nuxt is hydrating to keep web vital metrics accurate.
+
+::script-types
+::
+
+You can access the `track` and `pageview` methods as a proxy directly or await the `$script` promise to access the object. It's recommended to use the proxy for any void functions.
+
+::code-group
+
+```ts [Proxy]
+const { proxy } = useScriptVercelAnalytics()
+proxy.track('signup', { plan: 'pro' })
+```
+
+```ts [onLoaded]
+const { onLoaded } = useScriptVercelAnalytics()
+onLoaded(({ track }) => {
+ track('signup', { plan: 'pro' })
+})
+```
+
+::
+
+## Example
+
+Loading Vercel Analytics through `app.vue` when Nuxt is ready.
+
+```vue [app.vue]
+
+```
+
+### Manual Tracking
+
+If you want full control over what gets tracked, disable automatic tracking and call `track` / `pageview` manually.
+
+```vue [app.vue]
+
+```
+
+### beforeSend
+
+Use `beforeSend` to filter or modify events before they reach Vercel. Return `null` to cancel an event.
+
+```vue [app.vue]
+
+```
diff --git a/docs/content/scripts/content/vimeo-player.md b/docs/content/scripts/vimeo-player.md
similarity index 65%
rename from docs/content/scripts/content/vimeo-player.md
rename to docs/content/scripts/vimeo-player.md
index 65fce519..28c65efc 100644
--- a/docs/content/scripts/content/vimeo-player.md
+++ b/docs/content/scripts/vimeo-player.md
@@ -1,4 +1,5 @@
---
+
title: Vimeo Player
description: Show performance-optimized Vimeo videos in your Nuxt app.
links:
@@ -10,17 +11,33 @@ links:
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptVimeoPlayer.vue
size: xs
+
---
[Vimeo](https://vimeo.com/) is a video hosting platform that allows you to upload and share videos.
-Nuxt Scripts provides a `useScriptVimeoPlayer` composable and a headless `ScriptVimeoPlayer` a component to interact with the Vimeo Player.
+Nuxt Scripts provides a [`useScriptVimeoPlayer()`{lang="ts"}](/scripts/vimeo-player){lang="ts"} composable and a headless [``{lang="html"}](/scripts/vimeo-player){lang="html"} a component to interact with the Vimeo Player.
+
+::script-stats
+::
-## ScriptVimeoPlayer
+::script-types
+::
-The `ScriptVimeoPlayer` component is a wrapper around the `useScriptVimeoPlayer` composable. It provides a simple way to embed Vimeo videos in your Nuxt app.
+## Types
-It's optimized for performance by leveraging the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading the Vimeo Player when the specific elements events happen.
+To use Video Player with full TypeScript support, you will need
+to install the `@types/vimeo__player` dependency.
+
+```bash
+pnpm add -D @types/vimeo__player
+```
+
+## [``{lang="html"}](/scripts/vimeo-player){lang="html"}
+
+The [``{lang="html"}](/scripts/vimeo-player){lang="html"} component is a wrapper around the [`useScriptVimeoPlayer()`{lang="ts"}](/scripts/vimeo-player){lang="ts"} composable. It provides a simple way to embed Vimeo videos in your Nuxt app.
+
+It's optimized for performance by using the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading the Vimeo Player when the specific elements events happen.
By default, it will load on the `mousedown` event.
@@ -63,50 +80,6 @@ async function play() {
::
-### Props
-
-The `ScriptVimeoPlayer` component accepts the following props:
-
-- `trigger`: The trigger event to load the Vimeo Player. Default is `mousedown`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
-- `aboveTheFold`: Optimizes the placeholder image for above-the-fold content. Default is `false`.
-- `rootAttrs`: Override the root attributes that are automatically set.
-- `placeholderAttrs`: The attributes for the placeholder image. Default is `{ loading: 'lazy' }`.
-- `id`: Shorthand for `vimeoOptions.id`.
-- `url`: Shorthand for `vimeoOptions.url`.
-- `vimeoOptions`: All options from the Player SDK are supported, please consult the [Embed Options](https://developer.vimeo.com/player/sdk/embed)
-for full documentation.
-
-```ts
-interface VimeoPlayerProps {
- id: number | undefined
- url?: string | undefined
- autopause?: boolean | undefined
- autoplay?: boolean | undefined
- background?: boolean | undefined
- byline?: boolean | undefined
- color?: string | undefined
- controls?: boolean | undefined
- dnt?: boolean | undefined
- height?: number | undefined
- interactive_params?: string | undefined
- keyboard?: boolean | undefined
- loop?: boolean | undefined
- maxheight?: number | undefined
- maxwidth?: number | undefined
- muted?: boolean | undefined
- pip?: boolean | undefined
- playsinline?: boolean | undefined
- portrait?: boolean | undefined
- responsive?: boolean | undefined
- speed?: boolean | undefined
- quality?: VimeoVideoQuality | undefined
- texttrack?: string | undefined
- title?: boolean | undefined
- transparent?: boolean | undefined
- width?: number | undefined
-}
-```
-
#### Eager Loading Placeholder
The Vimeo Video placeholder image is lazy-loaded by default. You should change this behavior if your video is above the fold
@@ -134,7 +107,7 @@ See the [Facade Component API](/docs/guides/facade-components#facade-components-
### Events
-The `ScriptVimeoPlayer` component emits all events from the Vimeo Player SDK. Please consult the [Player Events](https://developer.vimeo.com/player/sdk/reference#about-player-events) for full documentation.
+The [``{lang="html"}](/scripts/vimeo-player){lang="html"} component emits all events from the Vimeo Player SDK. Please consult the [Player Events](https://developer.vimeo.com/player/sdk/reference#about-player-events) for full documentation.
```ts
const emits = defineEmits<{
@@ -168,11 +141,11 @@ const emits = defineEmits<{
### Slots
-As the component is provided headless, there are a number of slots for you to customize the player however you like before it's loaded in.
+As Nuxt provides the component headless, you can use slots to customize the player however you like before it loads.
**default**
-The default slot is used to display content that will always be visible.
+The default slot displays content that will always be visible.
```vue
@@ -186,7 +159,7 @@ The default slot is used to display content that will always be visible.
**awaitingLoad**
-The slot is used to display content while the video is loading.
+This slot displays content while the video is loading.
```vue
@@ -202,7 +175,7 @@ The slot is used to display content while the video is loading.
**loading**
-The slot is used to display content while the video is loading.
+This slot displays content while the video is loading.
```vue
@@ -218,7 +191,7 @@ The slot is used to display content while the video is loading.
**placeholder**
-The slot is used to display a placeholder image before the video is loaded. By default, this will show the
+This slot displays a placeholder image before the video loads. By default, this will show the
vimeo thumbnail for the video. You can display it however you like.
```vue
@@ -231,9 +204,9 @@ vimeo thumbnail for the video. You can display it however you like.
```
-## useScriptVimeoPlayer
+## [`useScriptVimeoPlayer()`{lang="ts"}](/scripts/vimeo-player){lang="ts"}
-The `useScriptVimeoPlayer` composable lets you have fine-grain control over the Vimeo Player SDK. It provides a way to load the Vimeo Player SDK and interact with it programmatically.
+The [`useScriptVimeoPlayer()`{lang="ts"}](/scripts/vimeo-player){lang="ts"} composable lets you have fine-grain control over the Vimeo Player SDK. It provides a way to load the Vimeo Player SDK and interact with it programmatically.
```ts
export function useScriptVimeoPlayer(_options?: VimeoPlayerInput) {}
@@ -241,16 +214,6 @@ export function useScriptVimeoPlayer(_options?: VimeoP
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-### VimeoPlayerApi
-
-```ts
-export interface VimeoPlayerApi {
- Vimeo: {
- Player: ScriptVimeoPlayer
- }
-}
-```
-
## Example
Loading the Vimeo Player SDK and interacting with it programmatically.
diff --git a/docs/content/scripts/x-embed.md b/docs/content/scripts/x-embed.md
new file mode 100644
index 00000000..bb5928f6
--- /dev/null
+++ b/docs/content/scripts/x-embed.md
@@ -0,0 +1,192 @@
+---
+
+title: X Embed
+description: Server-side rendered X (Twitter) embeds with zero client-side API calls.
+links:
+ - label: ScriptXEmbed
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptXEmbed.vue
+ size: xs
+
+---
+
+[X (formerly Twitter)](https://x.com) is a social media platform for sharing posts.
+
+Nuxt Scripts provides a [``{lang="html"}](/scripts/x-embed){lang="html"} component that fetches tweet data server-side and exposes it via slots for complete styling control. All data is proxied through your server - no client-side API calls to X.
+
+::script-stats
+::
+
+::script-types
+::
+
+## Setup
+
+To use the X embed component, you must enable it in your `nuxt.config`:
+
+```ts [nuxt.config.ts]
+export default defineNuxtConfig({
+ scripts: {
+ registry: {
+ xEmbed: true,
+ },
+ },
+})
+```
+
+This registers the required server API routes (`/_scripts/embed/x` and `/_scripts/embed/x-image`) that handle fetching tweet data and proxying images.
+
+## [``{lang="html"}](/scripts/x-embed){lang="html"}
+
+The [``{lang="html"}](/scripts/x-embed){lang="html"} component is a headless component that:
+- Fetches tweet data server-side via the X syndication API
+- Proxies all images through your server for privacy
+- Exposes tweet data via scoped slots for custom rendering
+- Caches responses for 10 minutes
+
+### Demo
+
+::code-group
+
+:x-embed-demo{label="Output"}
+
+```vue [Basic Usage]
+
+
+
+
+
+
+
+```
+
+::
+
+### Slot Props
+
+The default slot receives the following props:
+
+```ts
+interface SlotProps {
+ // Raw data
+ tweet: XEmbedTweetData
+ // User info
+ userName: string
+ userHandle: string
+ userAvatar: string // Proxied URL
+ userAvatarOriginal: string // Original X URL
+ isVerified: boolean
+ // Tweet content
+ text: string
+ // Formatted values
+ datetime: string // "12:47 PM · Feb 5, 2024"
+ createdAt: Date
+ likes: number
+ likesFormatted: string // "1.2K"
+ replies: number
+ repliesFormatted: string // "234"
+ // Media
+ photos?: Array<{
+ URL: string
+ proxiedURL: string
+ width: number
+ height: number
+ }>
+ video?: {
+ poster: string
+ posterProxied: string
+ variants: Array<{ type: string, src: string }>
+ }
+ // Links
+ tweetURL: string
+ userURL: string
+ // Quote tweet
+ quotedTweet?: XEmbedTweetData
+ // Reply context
+ isReply: boolean
+ replyToUser?: string
+ // Helpers
+ proxyImage: (URL: string) => string
+}
+```
+
+### Named Slots
+
+| Slot | Description |
+|------|-------------|
+| `default` | Main content with slot props |
+| `loading` | Shown while fetching tweet data |
+| `error` | Shown if tweet fetch fails, receives `{ error }` |
+
+## How It Works
+
+1. **Server-side fetch**: Tweet data is fetched from `cdn.syndication.twimg.com` during SSR
+2. **Image proxying**: All images are rewritten to proxy through `/_scripts/embed/x-image`
+3. **Caching**: Responses are cached for 10 minutes at the server level
+4. **No client-side API calls**: The user's browser never contacts X directly
+
+This approach is inspired by [Cloudflare Zaraz's embed implementation](https://blog.cloudflare.com/zaraz-supports-server-side-rendering-of-embeds/).
+
+## Privacy Benefits
+
+- No third-party JavaScript loaded
+- No cookies set by X
+- No direct browser-to-X communication
+- User IP addresses not shared with X
+- All content served from your domain
diff --git a/docs/content/scripts/x-pixel.md b/docs/content/scripts/x-pixel.md
new file mode 100644
index 00000000..10aadb96
--- /dev/null
+++ b/docs/content/scripts/x-pixel.md
@@ -0,0 +1,24 @@
+---
+
+title: X Pixel
+description: Use X Pixel in your Nuxt app.
+links:
+- label: Source
+ icon: i-simple-icons-github
+ to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/x-pixel.ts
+ size: xs
+
+---
+
+[X Pixel](https://x.com/) lets you collect, clean, and control your customer data. X helps you to understand your customers and personalize their experience.
+
+Nuxt Scripts provides a registry script composable [`useScriptXPixel()`{lang="ts"}](/scripts/x-pixel){lang="ts"} to easily integrate X Pixel in your Nuxt app.
+
+::script-stats
+::
+
+::script-docs
+::
+
+::script-types
+::
diff --git a/docs/content/scripts/content/youtube-player.md b/docs/content/scripts/youtube-player.md
similarity index 60%
rename from docs/content/scripts/content/youtube-player.md
rename to docs/content/scripts/youtube-player.md
index 33af66f0..eab41175 100644
--- a/docs/content/scripts/content/youtube-player.md
+++ b/docs/content/scripts/youtube-player.md
@@ -1,4 +1,5 @@
---
+
title: YouTube Player
description: Show performance-optimized YouTube videos in your Nuxt app.
links:
@@ -10,17 +11,33 @@ links:
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptYouTubePlayer.vue
size: xs
+
---
[YouTube](https://youtube.com/) is a video hosting platform that allows you to upload and share videos.
-Nuxt Scripts provides a `useScriptYouTubePlayer` composable and a headless `ScriptYouTubePlayer` component to interact with the YouTube Player.
+Nuxt Scripts provides a [`useScriptYouTubePlayer()`{lang="ts"}](/scripts/youtube-player){lang="ts"} composable and a headless [``{lang="html"}](/scripts/youtube-player){lang="html"} component to interact with the YouTube Player.
-## ScriptYouTubePlayer
+::script-stats
+::
-The `ScriptYouTubePlayer` component is a wrapper around the `useScriptYouTubePlayer` composable. It provides a simple way to embed YouTube videos in your Nuxt app.
+::script-types
+::
-It's optimized for performance by leveraging the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading the YouTube Player when the specific elements events happen.
+## Types
+
+To use [YouTube](https://youtube.com) with full TypeScript support, you will need
+to install the `@types/youtube` dependency.
+
+```bash
+pnpm add -D @types/youtube
+```
+
+## [``{lang="html"}](/scripts/youtube-player){lang="html"}
+
+The [``{lang="html"}](/scripts/youtube-player){lang="html"} component is a wrapper around the [`useScriptYouTubePlayer()`{lang="ts"}](/scripts/youtube-player){lang="ts"} composable. It provides a simple way to embed YouTube videos in your Nuxt app.
+
+It's optimized for performance by using the [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers), only loading the YouTube Player when the specific elements events happen.
By default, it will load on the `mousedown` event.
@@ -55,7 +72,7 @@ function stateChange(event) {
-
+
Play Video
@@ -66,29 +83,33 @@ function stateChange(event) {
::
-### Props
+### Privacy
-The `ScriptYouTubePlayer` component accepts the following props:
+The ``{lang="html"} component is privacy-friendly by default and sets the video host to `https://www.youtube-nocookie.com`.
-- `trigger`: The trigger event to load the YouTube Player. Default is `mousedown`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
-- `placeholderAttrs`: The attributes for the placeholder image. Default is `{ loading: 'lazy' }`.
-- `aboveTheFold`: Optimizes the placeholder image for above-the-fold content. Default is `false`.
+To modify this behavior, you can set the `host` prop to `https://www.youtube.com`.
-All script options from the [YouTube IFrame Player API](https://developers.google.com/youtube/iframe_api_reference) are supported on the `playerVars` prop, please consult the [Supported paramters](https://developers.google.com/youtube/player_parameters#Parameters) for full documentation.
+```vue
+
+```
-```ts
-export interface YouTubeProps {
- // YouTube Player
- videoId: string
- playerVars?: YT.PlayerVars
- width?: number
- height?: number
-}
+### Placeholder
+
+The YouTube Player placeholder image is 1280x720 webp that is lazy-loaded by default.
+
+To modify the placeholder size you can set the `thumbnailSize` prop, if you'd prefer
+to use a `jpg` you can pass the `webp` prop as `false`.
+
+```vue
+
```
-#### Eager Loading Placeholder
+If you need fine control over the placeholder you can set `placeholderAttrs` prop or override it using
+the `#placeholder` slot.
+
+#### Eager Loading
-The Vimeo Video placeholder image is lazy-loaded by default. You should change this behavior if your video is above the fold
+You should change this behavior if your video is above the fold
or consider using the `#placeholder` slot to customize the placeholder image.
::code-group
@@ -113,7 +134,7 @@ See the [Facade Component API](/docs/guides/facade-components#facade-components-
### Events
-The `ScriptYouTubePlayer` component emits all events from the YouTube Player SDK. Please consult the [Player Events](https://developers.google.com/youtube/iframe_api_reference#Events) for full documentation.
+The [``{lang="html"}](/scripts/youtube-player){lang="html"} component emits all events from the YouTube Player SDK. Please consult the [Player Events](https://developers.google.com/youtube/iframe_api_reference#Events) for full documentation.
```ts
const emits = defineEmits<{
@@ -122,16 +143,17 @@ const emits = defineEmits<{
'playback-quality-change': [e: YT.OnPlaybackQualityChangeEvent, target: YT.Player]
'playback-rate-change': [e: YT.OnPlaybackRateChangeEvent, target: YT.Player]
'error': [e: YT.OnErrorEvent, target: YT.Player]
+ 'api-change': [e: YT.PlayerEvent, target: YT.Player]
}>()
```
### Slots
-As the component is provided headless, there are a number of slots for you to customize the player however you like before it's loaded in.
+As Nuxt provides the component headless, you can use slots to customize the player however you like before it loads.
**default**
-The default slot is used to display content that will always be visible.
+The default slot displays content that will always be visible.
```vue
@@ -145,7 +167,7 @@ The default slot is used to display content that will always be visible.
**awaitingLoad**
-The slot is used to display content while the video is loading.
+This slot displays content while the video is loading.
```vue
@@ -161,7 +183,7 @@ The slot is used to display content while the video is loading.
**loading**
-The slot is used to display content while the video is loading.
+This slot displays content while the video is loading.
```vue
@@ -177,8 +199,8 @@ The slot is used to display content while the video is loading.
**placeholder**
-The slot is used to display a placeholder image before the video is loaded. By default, this will show the
-youtube thumbnail for the video. You can display it however you like.
+This slot displays a placeholder image before the video loads. By default, this will show the
+YouTube thumbnail for the video. You can display it however you like.
```vue
@@ -190,9 +212,9 @@ youtube thumbnail for the video. You can display it however you like.
```
-## useScriptYouTubePlayer
+## [`useScriptYouTubePlayer()`{lang="ts"}](/scripts/youtube-player){lang="ts"}
-The `useScriptYouTubePlayer` composable lets you have fine-grain control over the YouTube Player SDK. It provides a way to load the YouTube Player SDK and interact with it programmatically.
+The [`useScriptYouTubePlayer()`{lang="ts"}](/scripts/youtube-player){lang="ts"} composable lets you have fine-grain control over the YouTube Player SDK. It provides a way to load the YouTube Player SDK and interact with it programmatically.
```ts
export function useScriptYouTubePlayer(_options?: YouTubePlayerInput) {}
@@ -200,15 +222,6 @@ export function useScriptYouTubePlayer(_options?: Yo
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
-### YouTubePlayerApi
-
-```ts
-///
-export interface YouTubePlayerApi {
- YT: typeof YT
-}
-```
-
## Example
Loading the YouTube Player SDK and interacting with it programmatically.
@@ -218,7 +231,7 @@ Loading the YouTube Player SDK and interacting with it programmatically.
const video = ref()
const { onLoaded } = useScriptYouTubePlayer()
-let player
+const player = ref(null)
onLoaded(async ({ YT }) => {
// we need to wait for the internal YouTube APIs to be ready
const YouTube = await YT
@@ -233,12 +246,15 @@ onLoaded(async ({ YT }) => {
videoId: 'd_IFKP1Ofq0'
})
})
+function play() {
+ player.value?.playVideo()
+}
-
-
-
- Nuxt Scripts lets you load third-party scripts better performance, privacy, security and DX. It includes
- many popular third-parties out of the box.
-
-
-
- Nuxt Scripts provides an abstraction layer on top of third-party scripts, providing SSR support and type-safety and
- while still giving you full low-level control over how a script is loaded.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Speed up with Facade Components
-
-
- Nuxt Scripts provides several
- Facade Components
- out of the box.
-
-
- Facade components are fake UI elements that get replaced once a third-party script loads, they can significantly improve your performance while still providing a great user experience, however they do have
- trade-offs
- .
-
-
-
- First Contentful Paint
-
-
- Total Blocking Time
-
-
- Speed Index
-
-
-
- *Note that PageSpeed Insights lab data is a snapshot from a particular day, which tends to be variable. We will be updating this section with aggregated results and/or field data from production usage as soon as it's available.
-
-
-
-
-
-
-
-
-
- Cookie consent that's good enough to eat
-
-
- Nuxt Scripts aims to improve end-user privacy by providing a
- simple API for managing cookie consent
- .
-
-
- All scripts can be loaded conditionally based on user consent, set it up however you need.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A faster web collaboration
-
-
- Nuxt Scripts was designed and built by the Nuxt core team in collaboration with the Chrome Aurora team at Google.
-
-
- Nuxt Scripts is being actively maintained by the Nuxt core team and amazing community contributors, we welcome all contributions.
-
+ Privacy-focused analytics platform with session replay and web vitals tracking.
+
+
+
+
+
+
+
+
+ Issue #461 Test: Refresh Behavior
+
+
+
+
+
+
+
Refresh this page (Cmd+R / F5)
+
Immediately click "Track Immediate Event" before status becomes "loaded"
+
Check the Event Log - event should be queued and sent when script loads
+
Compare: Navigate away and back (SPA nav) - events should work immediately
+
+
+
+
+ Current Status:
+
+ {{ status }}
+
+
+
+
+
+ Track Event (no status check)
+
+ { proxy.identify(`user-${Date.now()}`); logEvent('proxy.identify() - NO STATUS CHECK', status) }"
+ >
+ Identify (no status check)
+
+ { proxy.pageview(); logEvent('proxy.pageview() - NO STATUS CHECK', status) }"
+ >
+ Pageview (no status check)
+
+
+
+
+ These buttons call proxy methods without checking status === 'loaded'.
+ Events are queued and flushed when script loads.
+
Nuxt Nation 2023: Daniel Roe - A New Nuxt - Release of Nuxt v3.8
@@ -28,5 +35,12 @@ function changeVideo() {
>
change video
+
+
+ change video needs fallback
+