Vue 3 Web Notification Component
A notification component is a super useful, re-usable component that I add to most web apps. I use this component to communicate with the user when an action has been completed, or something needs to be brought to their attention. I like to design this component globally so I can call it from anywhere within the application. In this tutorial we will be designing a simple Vue 3 notification component to display a success, warning, and failure notification to the user. You can style this notification however you like. We will just be focusing on the functionality.
Step 1: Build Your Event Bus
The first step in making this work is to build your event bus. In my opinion, every large Vue 3 Application should have an Event Bus. It just makes the process of communicating between pages and components a breeze. To create an event bus, you will have to add the mitt
package to simply emit and listen to events within your application:
yarn add mitt
Next, you will need to add a file named event-bus.js to the root of your javascript resources and add the following code:
/*
The event bus handles the communication between components.
*/
import mitt from 'mitt'
export const EventBus = mitt();
Now you can include this on any component and/or page that you want to emit an event from or listen to an event on. We will go through examples in the next steps so don’t worry!
Step 2: Build Your Component
Your notification component could be a variety of different styles, so we won’t get into the weeds with this post. However, what you will need is a basic Vue 3 component that you will register globally. I’d name this component something like AppNotification.vue
following the Vue 3 standards. Set up the base component to look like this:
<template>
<div v-show="show">
<img v-if="type == 'success'"/>
<img v-if="type == 'warning'"/>
<img v-if="type == 'failure'"/>
<span v-text="title"></span>
<span v-text="message"></span>
<button @click="performAction()"/>
</div>
</template>
<script>
import { EventBus } from '@/event-bus.js';
export default {
data(){
return {
show: false,
type: '',
title: '',
message: '',
action: ''
}
},
methods: {
performAction(){
}
}
}
</script>
Methods
The only method we have right now is the performAction()
method. This will run when the user clicks the button in the notification. we will shell this out a little further later on.
Data
There are 5 pieces of data that we will be using to make this component work and control the design. The first is show
. The show
variable is simply whether the component should be shown or not.
Next up we have the type
variable. This variable determines whether the notification is success
, warning
or failure
so you can display icons and colors to match what you need to convey to the user.
The next two variables are title
and message
. These are pretty self explanatory. The title
will be the title of the notification. Usually something like “Something Went Wrong” or “Account Created Successfully”. The message
variable is a more detailed description of the event such as “You have errors in your form, please re-submit” or “You can now authenticate with this account”.
Finally, we have the action
variable. Which, could be optional. It will simply allow the user to perform an action such as dismiss
or show-later
. We will come back and visit these variables soon since we will have to connect all of them.
Template
To touch a little on the template. We embed our variables into the display so we have full control on what should display from where we emit the variable. We also only show the notification when show = true
. Don’t worry, we will touch on that as well.
The image elements are there for an example of what you could do. Maybe show a green light when everything goes well, a yellow warning on a warning
notification and/or a red stop sign on a failure
. All of this will be dynamic depending on the notification context.
Next up, we have to register our component globally.
Step 3: Register your Component Globally
This is an important step since we want to be able to call this component from anywhere within our app. I usually register the component in the AppLayout.vue
or any wrapper you use for page layout. All you will need to do is import the component and add it in the template:
import AppNotification from '@/Components/Global/AppNotification.vue'
Make sure you import from wherever you saved the component. Then add the component to the bottom of your template:
<app-notification/>
</div>
While we won’t discuss much of the style of this component in this tutorial, it’s important to consider how you want your notification to be displayed. I’d recommend that this is a fixed element with a high z-index to display over any possible element and feel like an actual notification.
Step 4: Emitting the notify
Event
Let’s get to actually emitting a notification event, then we will get to the binding of it. Let’s say that somewhere in our application we have a form to create an account. Upon success or failure, this form will emit a notification.
In your component, you would first import the EventBus
:
import { EventBus } from '@/event-bus.js
When the form has completed processing you’d emit the following event:
EventBus.emit('notify', {
type: 'success',
title: 'Account Added',
message: 'You can now add transactions, set goals, and budget for this account!',
action: 'close'
});
You can see how this will map! We emit an event with the name notify
and some data. The data we will use to map locally within our AppNotification.vue
component allowing for full customization of the display. The form failed to submit? Change type
to failure
and so on.
Step 5: Listen to notify
Event
Now let’s jump back to our AppNotification.vue
component and handle these events. Since we’ve already imported our EventBus
, let’s start by adding the following:
mounted(){
this.bindEvents();
}
This will set up our component to listen to events when we define the bindEvents()
method.
Next, let’s add the following methods:
methods: {
bindEvents(){
EventBus.on('notify', function( data ){
this.handleNotification( data );
}.bind(this));
},
handleNotification( data ){
this.type = data.type;
this.title = data.title;
this.message = data.message;
this.action = data.action;
this.show = true;
setTimeout( function(){
this.clearNotification();
}.bind(this), 3000 );
},
runAction(){
switch( this.action ){
case 'close':
this.clearNotification();
break;
}
},
clearNotification(){
this.show = false;
setTimeout( function(){
this.type = '';
this.title = '';
this.message = '';
this.action = '';
}.bind(this), 1000 );
}
}
Looks like a lot going on, but let’s break it down.
bindEvents()
method
The bind events method just attaches an event listener to our EventBus
that listens for the notify
event. Whenever the notifiy
event comes through, we grab the data and hand it down to the handleNotification()
method.
handleNotification()
method
This method does two important things. First, it takes the data we send with the notify
event and sets it locally. By doing this, we can display any simple data from anywhere in the app to notify the user.
Next, this method handles how long the notification should be displayed. By using setTimeout()
we show the notification for 3 seconds before we call the clearNotification()
method which hides it. This is similar to how notification on all devices work.
clearNotification()
method
This method simply hides the notification and resets the data. There’s a 1 second time out in this method as well. If you have an animation that hides the notification, the notification will clear before it’s fully hidden leading to poor design.
runAction()
method
This method is customizable for your needs. Whenever the user interacts with the button on your notification you can perform a certain event. In this example, we have a close
action. This action just closes the notification before it closes by default.
Putting it all together, our notification’s functionality should look like:
import { EventBus } from '../../event-bus.js';
export default {
data(){
return {
show: false,
type: '',
title: '',
message: '',
action: ''
}
},
mounted(){
this.bindEvents();
},
methods: {
bindEvents(){
EventBus.on('notify', function( data ){
this.handleNotification( data );
}.bind(this));
},
handleNotification( data ){
this.type = data.type;
this.title = data.title;
this.message = data.message;
this.action = data.action;
this.show = true;
setTimeout( function(){
this.clearNotification();
}.bind(this), 3000 );
},
runAction(){
switch( this.action ){
case 'clear':
this.clearNotification();
break;
}
},
clearNotification(){
this.show = false;
setTimeout( function(){
this.type = '';
this.title = '';
this.message = '';
this.action = '';
}.bind(this), 1000 );
}
}
}
Step 6: Template basics
While we won’t go into the design aspects of the notification, there are a few rules you should follow or at least keep in mind. These apply to whether you do a pop up from the bottom of the screen, a top right notification, etc.
First, make sure your notification is displayed as a fixed
element. This will make sure that wherever the action takes place, the user will be able to see the notification. If the notification is an absolute
positioned element, the user may not see it if they are scrolled down on the page.
Next, animations add a better feel to the notification. It’s a great idea to add them. Vue makes this easy with their transition structure. If you are using TailwindUI’s notification, I believe they animate that out of the box.
Finally, make sure you use a very high z-index. You want the notification to appear above all other elements. Notifications should bring attention to the user. If the notification is behind elements, the user won’t see it.
Optional: Use with Laravel Echo
You could easily use this functionality with Laravel Echo. All you’d have to do is listen to the Echo event and pass the result to the Notification component within your application. Something along the lines of:
mounted(){
Echo.private(`queue-job-finished`)
.listen('AccountAdded', (e) => {
EventBus.emit('notify', {
type: 'success',
title: 'Account Added',
message: 'You can now add transactions, set goals, and budget for this account!',
action: 'close'
});
})
}
Now, whenever the websocket emits an event, you can notify the user!
Conclusion
This is a fairly simple component, that’s extremely powerful and useful. We use components like this in a variety of different places. If you’d like to see an example of a larger application built with Vue 3, check out our book. With the purchase of the ultimate package, you can pull down the source code with ROAST and see how we’ve structured a single page application that works on both web and mobile. If you have any questions about Vue 3 or anything else, hit us up on the community forum or on Twitter.