Vue 3 Web Notification Component

Dan Pastori avatar
Dan Pastori March 21st, 2022

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.

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Gold Sponsor SnapShooter Backups

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We’re privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.

What topics are you interested in?
Are you interested in our upcoming book?