Composables
useScript
Load third-party scripts with SSR support and a proxied API.
Experimental in v1.8
Warn: The API is still experimental and may change in the future.
Use non-bundled IIFE third-party scripts a breeze with a focus on performance and DX.
It's recommended to use when you want to use any non-blocking third-party: Google Analytics, Stripe, etc. However,
it is low-level and is better served as a building block for higher-level APIs useGoogleAnalytics
, etc.
Features:
- 🦥 Lazy, but fast:
defer
,fetchpriority: 'low'
, early connections (preconnect
,dns-prefetch
) - ☕ Loading strategies:
idle
,manual
- 🪨 Single script instance for your app
- 🎃 Events for SSR scripts:
onloading
,onerror
, etc - 🪝 Proxy API: call the script functions before it's loaded, noop for SSR, stubbable, etc
- 🇹 Fully typed APIs
Background
Loading scripts using the useHead
composable is easy.
useHead({
scripts: [
{ src: 'https://www.google-analytics.com/analytics.js' }
]
})
However, when loading a third-party script, you often want to access some functionality provided by the script.
For example, Google Analytics provides a gtag
function that you can use to track events.
// We need to load first: https://www.google-analytics.com/analytics.js
gtag('event', 'page_view', {
page_title: 'Home',
page_location: 'https://example.com',
page_path: '/',
})
The API provided by these scripts doesn't work in a SSR environment or if the script isn't loaded yet. Leading to a jumbled mess of trying to make sure we can use the API. For TypeScript you'll need to augment global window types to use the API effectively.
The useScript composable aims to solve these issues and make working with third-party scripts a breeze.
interface GoogleTag {
gtag: ((fn: 'event', opt: string, opt2: { [key: string]: string }) => void)
}
const { gtag } = useScript<GoogleTag>({
src: 'https://www.google-analytics.com/analytics.js',
}, {
trigger: 'idle',
use: () => { gtag: window.gtag },
})
// fully typed, usable in SSR and when lazy loaded
gtag('event', 'page_view', {
page_title: 'Home',
page_location: 'https://example.com',
page_path: '/',
})
We can even go a step further, say we want to send API requests on the server, we can conditionally stub the API.
const { gtag } = useScript<GoogleTag>({
src: 'https://www.google-analytics.com/analytics.js',
}, {
use: () => { gtag: window.gtag },
stub: () => {
if (process.server) {
return (fn: 'event', opt: string, opt2: { [key: string]: string }) => {
// send fetch to ga
return fetch('https://www.google-analytics.com/analytics.js', {
method: 'POST',
body: JSON.stringify({ event: opt, ...op2 })
})
}
}
}
})
Usage
Arguments
useScript<API>(scriptOptions, options)
scriptOptions
The script options, this is the same as the script
option for useHead
. For example src
, async
, etc.
useScript({
key: 'google-analytics', // custom key
src: 'https://www.google-analytics.com/analytics.js',
async: true,
defer: true,
})
options
skipEarlyConnections
Used to skip early connections such as dns-prefetch
and preconnect
.
Useful when you're loading a script from your own domain.
use
A function that resolves the scripts API. This is only called client-side.
const { trackPageview } = useScript<FathomApi>({
// fathom analytics
src: 'https://cdn.usefathom.com/script.js',
}, {
use: () => window.fathom
})
// just works
trackPageview({ url: 'https://example.com' })
trigger
An optional loading strategy to use. idle
or manual
. Defaults to undefined
.
// will load on idle
useScript({
src: 'https://example.com/script.js',
}, {
trigger: 'idle'
})
stub
A more advanced function used to stub out the logic of the API. This will be called on the server and client.
const { sendEvent, doSomething } = useScript<MyScriptApi>({
src: 'https://example.com/script.js',
}, {
use: () => window.myScript,
stub: ({ fn }) => {
// stub out behavior on server
if (process.server && fn === 'sendEvent')
return (opt: string) => fetch('https://api.example.com/event', { method: 'POST', body: opt })
}
})
// on server, will send a fetch to https://api.example.com/event
// on client it falls back to the real API
sendEvent('event')
// on server, will noop
// on client it falls back to the real API
doSomething()
$script
The return value is an object with the API provided by the script. It also contains a special $script
property
that gives you access to the underlying script instance.
const { $script } = useScript({
// ...
})
status
The status of the script. Can be one of the following: 'awaitingLoad' | 'loading' | 'loaded' | 'error'
loaded
A boolean for if the script has loaded.
load
Trigger the script to load. This is useful when using the manual
loading strategy.
const { $script } = useScript({
// ...
}, {
trigger: 'manual'
})
// ...
$script.load()
waitForLoad
A promise that resolves when the script is ready to use. This can be useful when you don't want to use the mock API.
const { $script, doSomething } = useScript({
// ...
}, { use: () => window.myScript })
// will only run client-side, once the script is ready
$script.waitForLoad().then(() => {
// ...
doSomething() // will always be called immediately
})
Examples
Google Analytics
import { useScript } from 'unhead'
const { gtag } = useScript({
src: 'https://www.google-analytics.com/analytics.js',
}, {
use: () => { gtag: window.gtag }
})