---
title: Script Loading
description: Learn how to efficiently load and manage scripts with Unhead's useScript composable
new: true
---

## Introduction

The `useScript` composable provides a powerful way to load and manage external scripts in your application. Built on top of `useHead()`{lang="ts"}, it offers advanced features for script loading, performance optimization, and safe script interaction.

## Script Singleton Pattern

A key feature of `useScript` is its singleton pattern - scripts with the same source or key are only loaded once globally, regardless of how many components request them.

```ts
import { useScript } from '@unhead/dynamic-import'

// In component A
useScript('https://maps.googleapis.com/maps/api/js')

// In component B - reuses the same script instance, doesn't load twice
useScript('https://maps.googleapis.com/maps/api/js')
```

### Creating Reusable Script Composables

For better organization and reuse, wrap script initialization in dedicated composables:

```ts
// composables/useGoogleMaps.ts
import { useScript } from '@unhead/dynamic-import'

export function useGoogleMaps(options = {}) {
  return useScript({
    src: 'https://maps.googleapis.com/maps/api/js',
    key: 'google-maps',
    ...options
  })
}
```

## Default Behavior & Performance

By default, `useScript` is configured for optimal performance and privacy:

### Performance Attributes

- Scripts load after hydration by default for better performance
- `async: true` - Load without blocking render
- `defer: true` - Execute in document order after page has loaded
- `fetchpriority: 'low'` - Prioritize other critical resources first

### Privacy Attributes

- `crossorigin: 'anonymous'` - Prevent third-party cookie access
- `referrerpolicy: 'no-referrer'` - Block referrer headers to script domain

## Proxied Function Calls

The `proxy` feature allows you to safely call script functions even before the script has loaded:

```ts
import { useScript } from '@unhead/dynamic-import'

const { proxy } = useScript('https://www.googletagmanager.com/gtag/js')

// Works immediately, even if script hasn't loaded yet
proxy.gtag('event', 'page_view')
```

These function calls are queued and executed once the script loads. If the script fails to load, the calls are silently dropped.

### Benefits of the Proxy Pattern

- Works during server-side rendering
- Resilient to script blocking (adblockers, etc.)
- Maintains function call order
- Allows script loading anytime without breaking application logic

### Limitations

- Cannot synchronously get return values from function calls
- May mask loading issues (script failing silently)
- More difficult to debug than direct calls
- Not suitable for all script APIs

### Direct API Access

For direct access to the script's API after loading:

```ts
import { useScript } from '@unhead/dynamic-import'

const { onLoaded } = useScript('https://www.googletagmanager.com/gtag/js')

onLoaded(({ gtag }) => {
  // Direct access to the API after script is loaded
  const result = gtag('event', 'page_view')
  console.log(result)
})
```

## Loading Triggers

Control when scripts load using triggers:

```ts
import { useScript } from '@unhead/dynamic-import'

// Load after a timeout
useScript('https://example.com/analytics.js', {
  trigger: new Promise(resolve => setTimeout(resolve, 3000))
})

// Load on user interaction
useScript('https://example.com/video-player.js', {
  trigger: (load) => {
    // Only runs on client
    document.querySelector('#video-container')
      ?.addEventListener('click', () => load())
  }
})

// Manual loading (useful for lazy loading)
const { load } = useScript('https://example.com/heavy-library.js', {
  trigger: 'manual'
})

// Load when needed
function handleSpecialFeature() {
  load()
  // Rest of the feature code...
}
```

## Resource Warmup Strategies

Optimize loading with resource hints to warm up connections before loading the script:

```ts
import { useScript } from '@unhead/dynamic-import'

useScript('https://example.com/script.js', {
  // Choose a strategy
  warmupStrategy: 'preload' | 'prefetch' | 'preconnect' | 'dns-prefetch'
})
```

### Strategy Selection Guide

- `preload` - High priority, use for immediately needed scripts
- `prefetch` - Lower priority, use for scripts needed soon
- `preconnect` - Establish early connection, use for third-party domains
- `dns-prefetch` - Lightest option, just resolves DNS
- `false` - Disable warmup entirely
- Function - Dynamic strategy based on conditions

### Manual Warmup Control

For granular control over resource warming:

```ts
import { useScript } from '@unhead/dynamic-import'

const script = useScript('https://example.com/video-player.js', {
  trigger: 'manual'
})

// Add warmup hint when user might need the script
function handleHoverVideo() {
  script.warmup('preconnect')
}

// Load when definitely needed
function handlePlayVideo() {
  script.load()
}
```

## Complete Example

```ts
import { useScript } from '@unhead/dynamic-import'

const analytics = useScript({
  src: 'https://example.com/analytics.js',
  key: 'analytics',
  defer: true,
  async: true,
  crossorigin: 'anonymous',
  referrerpolicy: 'no-referrer'
}, {
  warmupStrategy: 'preconnect',
  trigger: new Promise((resolve) => {
    // Load after user has been on page for 3 seconds
    setTimeout(resolve, 3000)
  })
})

// Track page view immediately (queued until script loads)
analytics.proxy.track('pageview')

// Access direct API after script is loaded
analytics.onLoaded(({ track }) => {
  // Do something with direct access
  const result = track('event', { category: 'engagement' })
  console.log('Event tracked:', result)
})

// Handle errors
analytics.onError((error) => {
  console.error('Failed to load analytics:', error)
})
```

## Best Practices

::tip
For effective script management:

- Use composables to encapsulate script initialization logic
- Consider user privacy when loading third-party scripts
- Use appropriate warmup strategies based on script importance
- Add error handling for critical scripts
- Use triggers to control loading timing for better performance
- Be mindful of proxy limitations for complex script APIs
::

## Common Use Cases

### Google Analytics

```ts
export function useGoogleAnalytics() {
  const script = useScript({
    src: 'https://www.googletagmanager.com/gtag/js',
    defer: true
  })

  // Initialize GA
  script.proxy.gtag('js', new Date())
  script.proxy.gtag('config', 'G-XXXXXXXXXX')

  return {
    ...script,
    trackEvent: (category, action, label) => {
      script.proxy.gtag('event', action, {
        event_category: category,
        event_label: label
      })
    }
  }
}
```