You're viewing Nuxt Scripts v0 (stable) documentation. View latest (v1 beta) docs →
Guides

Bundling Remote Scripts

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.
// GOOD - Static values allow for bundling
useScript('https://example.com/script.js', {
  bundle: true
})
// BAD - Dynamic values prevent bundling
useScript(scriptSrc, {
  bundle: canBundle
})
  1. 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.

// 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',
})

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 all scripts to be bundled by default.

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:

// ✅ Bundled at build-time (static values)
useScript('https://example.com/script.js', { bundle: true })

// ❌ Cannot be bundled (dynamic values)
const scriptUrl = computed(() => getScriptUrl())
useScript(scriptUrl, { bundle: dynamic.value })

Runtime Behavior

At runtime, bundled scripts behave differently:

// Original code
useScript('https://example.com/script.js', { bundle: true })

// After build transformation
useScript('/_scripts/abc123.js', {})

Important: Once bundled, 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 completely static values:

// 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 })

Manual Injection Patterns

When automatic bundling isn't possible, you can manually inject bundled scripts:

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:

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) {
  // This won't be bundled, but 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 scripts are bundled and cached.

nuxt.config.ts
export default defineNuxtConfig({
  scripts: {
    assets: {
      prefix: '/_custom-script-path/',
      cacheMaxAge: 86400000, // 1 day in milliseconds
    }
  }
})

Available Options

  • prefix - Custom path where bundled scripts are served (default: /_scripts/)
  • cacheMaxAge - Cache duration for bundled scripts in milliseconds (default: 7 days)

Cache Behavior

The bundling system uses two different cache strategies:

  • Build-time cache: Controlled by cacheMaxAge (default: 7 days). Scripts older than this are re-downloaded during builds to ensure freshness.
  • Runtime cache: Bundled scripts are served with 1-year cache headers since they are content-addressed by hash.

This dual approach ensures both build performance and reliable browser caching.