TypeScript
Composables

useScript()

The useScript composable provides an enhanced developer experience for loading third-party scripts with intelligent defaults for performance, security, and lifecycle management.

Basic Usage

import { useScript } from 'unhead'

const { onLoaded } = useScript('https://example.com/script.js')

onLoaded(() => {
  // Script loaded successfully
  console.log('Script is ready to use')
})

Smart Defaults

A singleton pattern is implemented so scripts with the same src or key are only loaded once globally. This helps prevent duplicate script loading and ensures consistent initialization.

The following defaults are applied for optimal performance and security:

  • Scripts load after hydration by default
  • defer enabled for proper execution order
  • fetchpriority="low" to prioritize critical resources
  • crossorigin="anonymous" prevents cookie access
  • referrerpolicy="no-referrer" blocks referrer headers

Input Options

Simple URL

Pass a URL string for the quickest implementation:

import { useScript } from 'unhead'

useScript(unheadInstance, 'https://example.com/script.js')

Full Configuration

Pass an options object to customize any <script> attribute:

import { useScript } from 'unhead'

useScript(unheadInstance, {
  src: 'https://example.com/script.js',
  id: 'my-script',
  async: true,
  defer: false,
  crossorigin: false, // disable crossorigin='anonymous'
  // Any valid script attribute can be used
})

Loading Control

Fine-tune when and how scripts load with the second parameter:

import { useScript } from 'unhead'

useScript(unheadInstance, 'https://example.com/script.js', {
  // When to load the script
  trigger: 'client', // | 'server' | Promise | ((load) => void)

  // Resource hint strategy
  warmupStrategy: 'preload', // | 'prefetch' | 'preconnect' | 'dns-prefetch',

  // Access the script's API
  use: () => window.externalAPI
})

Loading Triggers

Control precisely when scripts load with different trigger strategies:

import { useScript } from 'unhead'

// Load immediately on the client (default)
useScript(srcunheadInstance, , { trigger: 'client' })

// Load during server rendering
useScript(srcunheadInstance, , { trigger: 'server' })

// Load when a promise resolves
useScript(srcunheadInstance, , {
  trigger: new Promise(resolve =>
    setTimeout(resolve, 3000)
  )
})

// Custom load function
useScript(srcunheadInstance, , {
  trigger: (load) => {
    document.getElementById('load-button').addEventListener('click', load)
  }
})

Script Lifecycle

The script passes through these lifecycle states:

  • awaitingLoad - Initial state
  • loading - Script is loading
  • loaded - Script loaded successfully
  • error - Script failed to load
  • removed - Script was removed

Monitor these states with lifecycle hooks:

import { useScript } from 'unhead'

const script = useScript('https://example.com/script.js')

script.onLoaded((api) => {
  // Script loaded successfully
  console.log('Script is ready')
})

script.onError(() => {
  // Script failed to load
  console.error('Script loading failed')
})

Resource Hints

The warmupStrategy option automatically adds resource hints to optimize loading:

import { useScript } from 'unhead'

useScript(unheadInstance, 'https://example.com/script.js', {
  // Preload - highest priority, load ASAP
  warmupStrategy: 'preload',

  // Prefetch - load when browser is idle
  warmupStrategy: 'prefetch',

  // Preconnect - setup connection early
  warmupStrategy: 'preconnect',

  // DNS Prefetch - resolve DNS early
  warmupStrategy: 'dns-prefetch'
})
Choose the right strategy based on how critical the script is:
  • Use preload for essential scripts needed soon after page load
  • Use prefetch for scripts needed later in the user journey
  • Use preconnect or dns-prefetch to optimize third-party domains

API Proxying

If you need to access the script's API before it loads, use the use option with proxy support:

import { useScript } from 'unhead'

const script = useScript({
  src: 'https://maps.googleapis.com/maps/api/js'
}, {
  use: () => window.google.maps
})

// Works before script loads!
const map = script.proxy.Map()

The proxy records all calls and replays them once the script is loaded, allowing you to use the API immediately in your code without worrying about loading state.

Common Use Cases

Google Analytics

import { useScript } from 'unhead'

useScript(unheadInstance, {
  src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX',
  async: true
}, {
  trigger: 'client'
})

// Initialize gtag
useHead(unheadInstance, {
  script: [
    {
      children: `
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'G-XXXXXXXXXX');
      `,
      key: 'gtag-config'
    }
  ]
})

Loading on User Interaction

import { useScript } from 'unhead'

// Load YouTube player only when user clicks play
useScript(unheadInstance, 'https://www.youtube.com/iframe_api', {
  trigger: (load) => {
    document.getElementById('play-video').addEventListener('click', () => {
      load()
      // Show loading indicator while script is loading
    })
  }
})

API Reference

Input

  • src: String URL or object with script attributes

Options

  • trigger: When to load the script ('client', 'server', Promise, or custom function)
  • warmupStrategy: Resource hint strategy ('preload', 'prefetch', 'preconnect', 'dns-prefetch')
  • use: Function to access the script's API

Return Value

Returns a script controller object with these properties:

  • status: Current lifecycle state
  • onLoaded: Register callback for successful load
  • onError: Register callback for loading failure
  • proxy: Proxy to the script's API (if use option provided)
Did this page help you?