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-confetti
script 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 have been exported as 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',
})
Loading the script
Within your 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 being loaded.
Resolving the third-party script API
Now that the script is loaded, you can use it in your component. To do so we need to tell the underlying API how to use the script, for this we can leverage the use function.
This function is only called on the client-side and is used 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 new window.JSConfetti()
needs to be called 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>
Congrats! You should see some emojis once the script is loaded 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: Event 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.
In this example we'll combine the useScriptTriggerElement composable with the useScriptNpm
composable to load the script after a certain element is in view.
<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.