Nuxt.js, like Vue, supports plugins, which can be of three types: custom-built plugins, Vue plugins, and external packages and modules. While the Nuxt docs discuss the last two in detail, they only briefly explain how to build and use a custom plugin in your Nuxt application.
A global custom plugin can come in handy in a couple of situations. For example, let’s imagine that you can’t find an open source plugin for a feature you’re trying to implement, or you’re not completely satisfied with the ones you found. Or, maybe you’re feeling a little bit creative and you have some time on your hands.
In this tutorial, we’ll cover two different options to build our own custom plugin, using both Vue and JavaScript. As an example, our custom plugin will display my birthday in the console of your Nuxt app, but you can personalize it to fit your project’s needs. Let’s get started!
The first step is to build the Vue component. For this article, we’ll use a basic toast component that will display simple success and error messages. I’ve already built one for this article, and we’ll go over it in detail later:
<template> <div class="toast-element"> <div :class="['toast-container', { success: state === 'success', error: state === 'error' }]"> <div class="icon"> <svg v-if="state === 'success'" width="8" height="7" viewBox="0 0 8 7" fill="none" xmlns="http://www.w3.org/2000/svg" > <path d="M6.96304 0.962891L2.88896 5.37647L1.03711 3.3703" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /> </svg> <svg v-else width="3" height="10" viewBox="0 0 3 10" fill="none" xmlns="http://www.w3.org/2000/svg" > <path d="M1 1.6665V4.99984" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> <path d="M1 8.3335H1.00833" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </svg> </div> <p> {{ message }} </p> </div> </div> </template> <script> export default { data () { return { toastIsVisible: false, message: '', state: '' } }, mounted () { this.$store.subscribe((mutation, state) => { if (mutation.type === 'toast/showToast') { this.message = state.toast.content this.state = state.toast.state this.toastIsVisible = true document.querySelector('.toast-element').classList.add('enter') setTimeout(() => { if (this.toastIsVisible) { this.removeToast() } }, 3000) } }) }, methods: { removeToast () { document.querySelector('.toast-element').classList.replace('enter', 'leave') setTimeout(() => { this.toastIsVisible = false }, 240) } } } </script> <style scoped> .toast-element { position: fixed; top: -90px; left: 0; width: 100%; height: max-content; display: flex; justify-content: center; align-items: center; z-index: 7; opacity: 0; transition: all 240ms cubic-bezier(0, 0.0, 0.2, 1) 0s; } .enter { top: 20px; opacity: 1; } .leave { top: 20px; transform: scale(0.9); } .toast-container { width: fit-content; border: 1px solid transparent; border-radius: 5px; padding: 0.9375rem 1.875rem; display: grid; grid-template-columns: repeat(2, max-content); column-gap: 0.9375rem; align-items: center; } .success { border-color: #037F10; background-color: #F4FFF6; } .error { border-color: #FF2525; background-color: #FFF4F4; } .icon { border-radius: 50%; display: flex; } .success .icon { padding: 6px; background-color: #037F10; } .error .icon { padding: 5px 8px 5px 9px; background-color: #FF2525; } p { width: max-content; font-size: 1.0625rem; font-weight: 400; } .success p { color: #037F10; } .error p { color: #FF2525; } </style>
The toast component creates two buttons that look like the following:
If you review the toast component, you’ll notice a call to the store’s subscribe()
method. Because our component is dynamic, we’ll need to get the content from somewhere, namely, the store.
In our toast component, there are two variables, the toast message and its state, either success
or error
. With the help of Vuex, we’ll store the state in our Nuxt app. Therefore, we’ll need a store, preferably a different one from the app’s general store:
export const state = () => ({ content: '', state: '' }) export const mutations = { showToast (state, payload) { state.content = payload.content state.state = payload.state } }
Now that our store is ready, we need to set up a communication channel between the store and the toast component. Here, the subscribe()
method comes in handy, subscribing the component to the store’s mutations. Therefore, it is called after every mutation of the store, receiving the mutation and state objects as arguments. To learn more about the subscribe()
method, check out the subscribe section on the Vuex API page.
Because the subscribe()
method is called after each mutation of the store, we need to confirm that the toast store was mutated. Once we’ve confirmed that, we can go ahead and update the component’s variables, the message, and its state:
this.$store.subscribe((mutation, state) => { if (mutation.type === 'toast/showToast') { this.message = state.toast.content this.state = state.toast.state this.toastIsVisible = true document.querySelector('.toast-element').classList.add('enter') setTimeout(() => { if (this.toastIsVisible) { this.removeToast() } }, 3000) } })
We check that the mutation’s type is toast/showToast
because our toast store is in toast.js
and the mutation is the showToast()
method.
Now that we’re finished building the component and implementing its functionality, we need to make it available in the Nuxt app so that we can call a show
method to show the toast component when we need it; we’ll do so in the plugins
folder.
Making the plugin available across the app is also called injecting the plugin, and Vue provides the inject()
function for this purpose. This, in turn, prefixes the object of the show
method with a $
:
export default ({ app, store }, inject) => { inject('toaster', { showToast ({ content = '', state = '' }) { store.commit('toast/showToast', { content, state }) } }) }
Therefore, whenever we call this $toaster.showToast()
method in our Nuxt app, we need to pass in the content
and state
parameters as well.
However, if your plugin is for some JavaScript functionality, like in our example, displaying my birthday, then you’ll only need the code below:
export default ({ app }, inject) => { inject('displayBirthday', () => { console.log(`March 3rd!`) }) }
$displayBirthday()
will log my birthday to your console. For a case like this, you wouldn’t need a store or a component.
If you’ve paid close attention to the two code snippets, you’ll notice that the types of the second parameter of the inject()
function vary. The first one is an object, while the second is a function. The object type is useful when your plugin might require multiple actions, while the function is best for a single action. However, the object can have just a single method in it.
Before we render the plugin, our Nuxt app needs to confirm that the toast plugin exists. We can add the code for this using the plugins
property in the nuxt.config.js
file:
export default { plugins: [ { src: '~/plugins/toaster' } ] }
The plugin injection code is in the plugins/toaster.js
file.
With that, our plugin is ready to use! To see our plugin in action, first, the toast component needs to be present on the page. Then, we can call the $toaster.showToast()
method of our store and pass along the content
and state
parameters:
this.$toaster.showToast({ content: 'Successfully logged in!', state: 'success' })
In this article, we’ve learned how to build a global custom plugin in a Nuxt app using Vue and JavaScript. You can use this component to implement different types of functionalities in your application, and it comes in handy when you can’t find an open source plugin that fits your project’s needs. We also discussed how to go about using a component for the plugin and what to do when the plugin doesn’t need a component.
While our example for this tutorial demonstrated fairly simple functionality, displaying a date in the console, feel free to build on this project and customize it to fit your needs. For more information on Nuxt plugins, I recommend checking out the Nuxt Docs. Happy building!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]