Tutorial: Load js-confetti
This tutorial loads js-confetti from npm. You'll call it through a proxied function and add types for its browser API.
Background on useScriptNpm()
useScriptNpm() is a registry script built on useScript(). It loads browser-ready package files published to npm.
Most npm packages belong in package.json. Loading one on demand may instead require a dynamic import, a separate chunk, and sometimes build-time transpilation.
useScriptNpm() is useful for an occasional, non-critical browser script that already exposes a global API. Install packages your application uses throughout the codebase as normal dependencies.
The three snippets below load the same file at different abstraction levels.
useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
})
Loading the script
Call useScriptNpm() inside a component:
<script setup lang="ts">
useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
})
</script>
The browser's Network panel should now show the script request.
Resolving the third-party script API
Tell Nuxt Scripts how to resolve the script's client-side API with the use function:
<script setup lang="ts">
useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
// tell useScript how to resolve the third-party script
use() {
return { JSConfetti: window.JSConfetti }
},
},
})
</script>
Using the third-party script API
The js-confetti library exposes a JSConfetti class. Create an instance after the script loads, then reuse that instance for subsequent calls.
You can wait for the script explicitly or use a proxied function to queue a call until it is ready.
<script setup lang="ts">
const { onLoaded } = useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
use() {
return { JSConfetti: window.JSConfetti }
},
},
})
onLoaded(({ JSConfetti }) => {
// using the real API instance
const confetti = new JSConfetti()
confetti.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
</script>
addConfetti is still untyped, so the editor cannot check its arguments or offer completion.
Adding types
Pass a generic to useScriptNpm() and augment Window with the same API:
<script setup lang="ts">
export interface JSConfettiApi {
JSConfetti: {
new (config?: { canvas?: HTMLCanvasElement }): {
addConfetti: (options?: { emojis?: string[] }) => Promise<void>
}
}
}
declare global {
interface Window extends JSConfettiApi {}
}
const { onLoaded } = useScriptNpm<JSConfettiApi>({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
use() {
return { JSConfetti: window.JSConfetti }
},
},
})
onLoaded(({ JSConfetti }) => {
const confetti = new JSConfetti()
// Checked against JSConfettiApi
confetti.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
</script>
Delay script loading
Use trigger when the script should wait for application state, an event, or a timer.
See the Script Triggers guide for all available options.
Using a ref
A ref loads the script when its value becomes truthy.
<script setup lang="ts">
const shouldLoad = ref(false)
const { onLoaded } = useScriptNpm<JSConfettiApi>({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
trigger: shouldLoad,
use: () => ({ JSConfetti: window.JSConfetti }),
},
})
onLoaded(({ JSConfetti }) => {
const confetti = new JSConfetti()
confetti.addConfetti({ emojis: ['🎉', '🎊', '✨'] })
})
</script>
<template>
<button @click="shouldLoad = true">
Click to load confetti
</button>
</template>
trigger: computed(() => someCondition.value) or trigger: () => shouldLoad.value.Using element events
Use useScriptTriggerElement() to wait for an element interaction.
<script setup lang="ts">
const mouseOverEl = ref<HTMLElement | null>(null)
const { onLoaded } = useScriptNpm<JSConfettiApi>({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
trigger: useScriptTriggerElement({ trigger: 'mouseover', el: mouseOverEl }),
use: () => ({ JSConfetti: window.JSConfetti }),
},
})
onLoaded(({ JSConfetti }) => {
const confetti = new JSConfetti()
confetti.addConfetti({ emojis: ['L', 'O', 'A', 'D', 'E', 'D'] })
})
</script>
<template>
<div ref="mouseOverEl">
<h1>Hover over me to load the confetti</h1>
</div>
</template>
Bundle the script locally
Nuxt Scripts bundles statically analyzable useScriptNpm() files by default and serves them from /_scripts/assets/. This avoids the initial connection to the package CDN.
Set bundle: false if you want to load the file directly from the configured CDN instead.
<script setup lang="ts">
useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
bundle: false,
},
})
</script>
Without this opt-out, the Network panel shows the script loading from your application's server. See Key Concepts for more on script instances and proxied functions.