Tutorial: Load js-confetti
Introduction
In this tutorial, you will learn how to load the js-confetti script using the Nuxt Scripts module.
You'll learn about the following:
- What the
useScriptNpm()registry script is. - How to load the
js-confettiscript using it. - Adding types to loaded scripts.
- Using proxied functions to call the script.
Background on useScriptNpm()
To load the script, we'll be using the useScriptNpm().
This is a registry script, a supported
third-party integration built on top of the
useScript() composable that allows you to load scripts from npm.
When working with npm files, you'd typically include them as a node_module dependency in the package.json file. However,
optimizing the script loading of these scripts can be difficult, requiring a dynamic import of the module from a separate chunk and
loading it only when needed. It also slows down your build as the module needs to be transpiled.
The useScriptNpm() registry script abstracts this process, allowing you to load scripts that export immediately invokable functions,
with a single line of code .
In many instances it will still make more sense to include the script as a dependency in the package.json file, but for scripts that are not used often or
are not critical to the application, this can be a great alternative.
To begin with we can think of using this script as an alternative to the useHead composable. You can see an example of the abstraction
layers in the following code sample.
useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
})
useScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/js-confetti.browser.js')
useHead({
script: [
{ src: 'https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js' }
]
})
Loading the script
Within one of your components, you'll want to load the script. You can do this by using the useScriptNpm() registry script.
<script setup lang="ts">
useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
})
</script>
If you check your browser requests, you should see the script load.
Resolving the third-party script API
Now that the script loads, you can use it in your component. To do so we need to tell the basic API how to use the script, for this we can Use the use function.
This function runs only on the client-side to resolve the third-party script.
<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
Now that we have a way to resolve the third-party script API, we can start using it.
The js-confetti library requires us to instantiate a new instance of the JSConfetti class everytime it's used,
the most compatible way to handle this is to wait for the script to load explicitly.
However, we can also make use of proxied functions if we prefer an easier to use API. Note that this will break
when switching between pages as you must call new window.JSConfetti() between pages.
<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>
<script setup lang="ts">
const { proxy } = useScriptNpm({
packageName: 'js-confetti',
file: 'dist/js-confetti.browser.js',
version: '0.12.0',
scriptOptions: {
use: () => typeof window.JSConfetti !== 'undefined' && new window.JSConfetti()
}
})
onMounted(() => {
// just works
proxy.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
</script>
Congrats. You should see some emojis once the script loads in.
However, you'll notice that we have an issue with types here. The addConfetti function is not typed, so we don't get any intellisense or type checking.
Adding types
You can use the generic from the useScriptNpm() composable to add types to the script and add global types to the window object.
<script setup lang="ts">
export interface JSConfettiApi {
JSConfetti: {
new (): {
addConfetti: (options?: { emojis: string[] }) => 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 }
},
},
})
onMounted(() => {
onLoaded(({ JSConfetti }) => {
const confetti = new JSConfetti()
// fully typed!
confetti.addConfetti({ emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'] })
})
})
</script>
Bonus: Trigger-based script loading
You can delay the loading of the script by using the trigger option. This can be useful if you want to load the script after a certain event or time.
See the Script Triggers guide for all available options.
Using a Ref
The simplest approach is to use a ref - the script loads when the ref becomes truthy.
<script setup lang="ts">
const shouldLoad = ref(false)
const { onLoaded } = useScriptNpm({
// ..
scriptOptions: {
trigger: shouldLoad
}
})
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
You can also use the useScriptTriggerElement() composable to trigger loading based on element interactions.
<script setup lang="ts">
const mouseOverEl = ref<HTMLElement>()
const { onLoaded } = useScriptNpm({
// ..
scriptOptions: {
trigger: useScriptTriggerElement({ trigger: 'mouseover', el: mouseOverEl })
}
})
// ..
onMounted(() => {
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>
Bonus: Bundling the script
As the script is from npm and versioned, we can safely bundle it with our application. This will reduce the number of DNS requests needed, improving the performance of our application.
To bundle the script, you can use the bundle option.
<script setup lang="ts">
const script = useScriptNpm({
// ...
scriptOptions: {
bundle: true
}
})
// ..
</script>
You should see the script loaded in from your apps server.
Conclusion
In this tutorial, you learned how to load the js-confetti script using the useScriptNpm() registry script.
To learn more about the specific concepts you explored, check out the documentation for Key concepts.