Nwose Lotanna Web Developer and Writer

Using event bus in Vue.js to pass data between components

4 min read 1220

Learn how to use an event bus to pass data between components in Vue.js

Prerequisites

This post is suited for developers of all stages, including beginners.
Here are a few things you should already have before going through this article:

  • Node.js version 10.x and above installed. You can verify that you have this version by running the command below in your terminal/command prompt:
node -v
  • Visual Studio Code editor or a similar code editor.
  • Vue’s latest version installed globally on your machine
  • Vue CLI 3.0 installed on your machine. To do this, uninstall the old CLI version first:
npm uninstall -g vue-cli

then install the new one:

npm install -g @vue/cli
  • Download a Vue starter project here.
  • Unzip the downloaded project
  • Navigate into the unzipped file and run the command to keep all the dependencies up-to-date:
npm install

The emitter problem

Vue has a way of communicating between two child components through a parent component using event emitters.

When you set up an event in a child component and a listener in the parent component, the reaction is passed down through the parent to the nested components.

While this is a valuable solution, it can become clumsy as your project grows.

The solution: Event bus

Essentially, an event bus is a Vue.js instance that can emit events in one component, and then listen and react to the emitted event in another component directly — without the help of a parent component.

By definition, using an event bus is more efficient than using event emitters because it requires less code to run.

We’re going to create an event bus instance as a separate file, import it into the two components that are going to share data, and then allow the components to communicate through this shared instance in a safe, private channel.

This is commonly known as the publish-subscribe approach.

Demo

Today, we’re going to walk through the process of creating and using the event bus to facilitate communication between two components.

Getting started with the event bus

First, we want to create the event bus. We’ll do this inside our main.js file. After definition, your main.js file should look like this:

import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
export const bus = new Vue();
new Vue({
  render: h => h(App),
}).$mount('#app')

As you can see, we’ve created a new Vue instance — a secure abstraction where we can handle communication between components without involving the parent component in the correspondence.

Creating a new component

We need two child components to communicate. However, you’ll notice there’s only one test.vue component in your starter project.

Create a new file and call it test2.vue and paste the code block below inside it:

<template>
  <div>
  </div>
</template>
<script>
export default {
  name: 'Test2',
  props: {
    msg: String
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

Now, go to your App.vue file and import it like the Test.vue file. Register the file under components like this:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <Test v-bind:header="header"/>
    <Test2 v-bind:header="header"/>
  </div>
</template>
<script>
import Test from './components/Test.vue';
import Test2 from './components/Test2.vue';
export default {
  name: 'app',
  components: {
    Test, Test2
  },
  data (){
    return {
      header:'initial header'
    }
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Setting up events

Now that your two components are ready, you can set up the event through emission in the Test component while you listen to the event in the Test2 component.



Open your Test.vue file and copy the code block below into it:

<template>
  <div>
      <h1 v-on:click="changeHeader">{{header}}</h1>
  </div>
</template>
<script>
import { bus } from '../main'
export default {
  name: 'Test',
  props: {
    header:{
        type: String
    } 
  },
  methods: {
      changeHeader (){
          this.header = "changed header";
          bus.$emit('changeIt', 'changed header');
      }
  }
}
</script>

Here, you’ll see that the event bus was imported from main.js , the template displays one header element through props, and there is a click event on it that points to the logic in the methods section.

The manual change of the Test.vue component occurs inside the method section and emits an event through the event bus.

The statement tells Vue to emit an event called changeIt and pass the string changed header as argument.

Listening to events and reacting

After setting up the event, we need to make the second component listen and react to the event. Open your Test2.vue file and copy in the code block below:

<template>
  <div> <h1>{{header}}</h1>
  </div>
</template>
<script>
import { bus } from '../main';
export default {
  name: 'Test2',
  props: {
    header:{
        type: String
    } 
  },
  created (){
    bus.$on('changeIt', (data) => {
      this.header = data;
    })
  }
}
</script>

When the event bus imports, all we see inside the template is the interpolation symbol. There isn’t a Vue directive or bindings.

We’ll use a lifecycle hook to initialize the listening process as the app is mounted on the DOM. The lifecycle hook is called created as the application is initialized.

The $on statement is now listening to a changeIt event, passing the data argument down, and setting it as the new header.

A visual illustrating event buses in Vue.js

When you click the first header in the interface, both headers change.

Removing listeners

Vue automatically un-mounts and removes these listeners before the destruction of a Vue instance. However, if you want to manually destroy them, you can run this simple command:

bus.$off();

The complete code to this tutorial can be found here on GitHub.

Ensure components in your production Vue apps render as expected with .

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps – .

Conclusion

This has been an introduction to the event bus in Vue.js. The event bus serves as a safe way to achieve independent communication between components without passing through a central or parent component.

The event bus is also cleaner and involves less code than other approaches, providing a great abstracted platform.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Nwose Lotanna Web Developer and Writer

7 Replies to “Using event bus in Vue.js to pass data between…”

  1. Sounds good! But could be better solution pass the bus object as prop to the different components, isn’t it? To be able to decouple as much as possible to the “parent” or to be independent from the project.

    What do u think?

  2. I have that feeling that having an EventBus in Vue is an anti-pattern, especially if you’re using VueX but I can’t quite put my finger on it. At the point you want to be sharing data like that, wouldn’t it be better to use a store to handle all of those “events” / “mutations”?

    It’s not a well formed or versed opinion yet, but I’d be happy for some external thoughts on the matter.

  3. At this point do you even need the props? Why not just have header as a data property?

  4. I agree, there’s something that feels wrong about an EventBus.

    Perhaps because it feels like a global variable and difficult to manage the state of the events? How hard would it be to maintain a bus that 7 different components are listening/firing events to?

  5. From my experience, this eventBus approach will lead you down the flames of hell. 😀
    I agree this seems like a very convienent approach to avoid bubbling up through multiple components, but it doesn’t mean you should do it.
    Developers get confused whether this is better than passing down props/emitting events, and basically just go for the eventBus every time, even when the shouldn’t, just because it’s easy. After a few weeks, you will realize your code has just become a huge pile of noodles/spaghettis (take your pick :D) where developers (team of 7, hard to track everything) used the event bus to also pass properties down to the children, and the whole purpose of having self-contained components, with one-way data flow, that you can test in isolation, is just gone forever. You opened the world of X-way data flow, where the event handlers add their own concerns to the data before passing it to the next.
    In the long run, you will forget which component is responsible for owning the data, and where is your source of truth.
    As a solution, not a silver bullet but a good compromise, I’d recommend having a look at Vuex (or something redux-like)

Leave a Reply