Skip to main content
Guides

Registry Scripts

Registry scripts are preconfigured integrations for common third-party services. Browse the available integrations in the Script Registry.

Features

Safe initialization

Registry composables initialize the required global state before they add the external script.

Loading controls

Each registry entry declares the bundling, proxying, and Partytown capabilities it supports. Composable-driven integrations also accept a loading trigger.

Types

Registry scripts include types for their configuration and exposed API, so editors can complete calls such as gtag() without a separate global declaration.

Development-time validation

Registry scripts use Valibot schemas to validate configuration during development. For example, the Cloudflare Web Analytics schema rejects a token shorter than 32 characters.

export const CloudflareWebAnalyticsOptions = object({
  /**
   * The Cloudflare Web Analytics token.
   */
  token: pipe(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()),
})

Production builds omit the validation code. During development, Nuxt Scripts skips an invalid script and logs the schema issues.

Runtime config

Register a script in nuxt.config.ts and Nuxt Scripts creates public runtime config fields for the environment-backed inputs declared by that integration. You can then supply fields such as its ID or token through .env instead of hardcoding them.

NUXT_PUBLIC_SCRIPTS_CLOUDFLARE_WEB_ANALYTICS_TOKEN=YOUR_TOKEN

Usage

Disabling in development

Set a registry entry to mock when development code calls an API such as gtag, but you do not want to load the vendor script. Mock mode registers a manual context and skips option validation.

nuxt.config.ts
export default defineNuxtConfig({
  scripts: {
    registry: {
      googleTagManager: { trigger: 'onNuxtReady' },
    },
  },
  $development: {
    scripts: {
      registry: {
        googleTagManager: 'mock',
      },
    },
  },
})

Load multiple instances

Registry scripts deduplicate by src or key. Give each call a unique key when you need multiple instances with different configuration.

const { proxy: gaOne } = useScriptGoogleAnalytics({
  id: 'G-TR58L0EF8P',
})

const { proxy: gaTwo } = useScriptGoogleAnalytics({
  // without a key the first script instance will be returned
  key: 'gtag2',
  id: 'G-1234567890',
})

A custom key also changes the runtime config path. For key: 'gtag2', declare the matching path yourself:

export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      scripts: {
        gtag2: {
          id: '', // NUXT_PUBLIC_SCRIPTS_GTAG2_ID
        },
      },
    },
  },
})

Use script options and script input

Registry scripts expose the core useScript() inputs through two fields:

import { useTimeout } from '@vueuse/core'
import { useScriptGoogleAnalytics } from '#imports'

const ready = useTimeout(5000)
useScriptGoogleAnalytics({
  id: 'G-XXXXXXXX',
  // HTML attributes to pass to the script element
  scriptInput: {
    'data-test': 'true',
  },
  // useScript options used for advanced features
  scriptOptions: {
    trigger: ready,
  },
})

Reuse one instance

If several pages use the same integration, configure it once in app.vue or nuxt.config. Later composable calls return that script instance and do not need the options again.

export default defineNuxtConfig({
  scripts: {
    registry: {
      // The explicit trigger loads the script globally.
      fathomAnalytics: {
        site: 'SITE_ID',
        trigger: 'onNuxtReady',
      }
    }
  }
})

You can also keep the shared configuration in your own composable:

export function useFathomAnalytics() {
  return useScriptFathomAnalytics({
    site: 'SITE_ID',
  })
}

Extending the Script Registry

Use the scripts:registry hook in nuxt.config.ts to add an integration:

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: '<svg>...</svg>', // optional
        import: {
          name: 'useScriptMyAnalytics',
          from: resolve('./composables/useScriptMyAnalytics'),
        },
      })
    },
  },

  devtools: {
    enabled: true,
  },
})

Then create your custom script composable:

composables/useScriptMyAnalytics.ts
import type { RegistryScriptInput } from '#nuxt-scripts/types'
import { object, string } from 'valibot'
import { useRegistryScript } from '#nuxt-scripts/utils'

export interface MyAnalyticsApi {
  track: (event: string, data?: Record<string, any>) => void
  identify: (userId: string) => void
}

declare global {
  interface Window {
    MyAnalytics: MyAnalyticsApi & { init: (apiKey?: string) => void }
  }
}

// Schema for validation and DevTools metadata
export const MyAnalyticsSchema = object({
  apiKey: string(),
})

export type MyAnalyticsInput = RegistryScriptInput<typeof MyAnalyticsSchema>

export function useScriptMyAnalytics<T extends MyAnalyticsApi>(options?: MyAnalyticsInput) {
  return useRegistryScript<T, typeof MyAnalyticsSchema>('myAnalytics', resolvedOptions => ({
    scriptInput: {
      src: 'https://analytics.example.com/sdk.js',
    },
    schema: import.meta.dev ? MyAnalyticsSchema : undefined,
    scriptOptions: {
      ...options?.scriptOptions,
      use() {
        if (!window.MyAnalytics)
          return
        window.MyAnalytics.init(resolvedOptions.apiKey)
        return window.MyAnalytics as T
      },
    },
  }), options)
}

Using Custom Registry Scripts

Nuxt auto-imports the registered composable:

pages/index.vue
<script setup lang="ts">
// Auto-imported from your registry
const { proxy, status } = useScriptMyAnalytics({
  apiKey: 'your-api-key',
  scriptOptions: {
    trigger: 'onNuxtReady'
  }
})

// Use the script API
function trackClick() {
  proxy.track('button_click', { button: 'hero-cta' })
}
</script>

<template>
  <button @click="trackClick">
    Track This Click
  </button>
  <div>Status: {{ status }}</div>
</template>

DevTools Integration

When you include a validation schema, Nuxt Scripts uses its required fields to populate the script's DevTools metadata in development.