DEV Community

Cover image for Global Event Bus in Vue
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

Global Event Bus in Vue

When building Vue applications, especially those that involve multiple deeply nested components, one of the trickiest parts is communication. How do you send a signal from a child component to another unrelated component without passing props all the way down or lifting state too high up?

In Vue 2, the answer was often a global event bus — a simple but effective way of letting components “talk” to each other by emitting and listening to events. However, in Vue 3, this feature was removed as part of the breaking changes.

In this article, we’ll explore:

  • Why the classic event bus was removed in Vue 3
  • How to implement a global event bus using mitt
  • Alternatives such as Pinia for state management
  • Best practices when choosing between different solutions

Enjoy!

🤔 Why was the event bus removed in Vue 3?

In Vue 2, developers often relied on the $on, $emit, and $off methods of Vue instances to create a simple event bus for communication between components. However, with Vue 3, this functionality was removed as part of a breaking change.

👉 According to the official migration guide, the events API was removed to encourage more predictable state management patterns and reduce hidden dependencies between components.

But don’t worry — even though the event bus was removed, you can still achieve the same result with libraries like mitt.

🟢 Creating a Global Event Bus with Mitt

Mitt is a tiny (~200b) functional event emitter library that works perfectly as a global event bus in Vue 3 applications. Let’s see how to set it up.

Step 1: Install mitt

npm install mitt
Enter fullscreen mode Exit fullscreen mode

Step 2: Create an event bus file

Create a file called eventBus.js in your project:

// eventBus.js
import mitt from 'mitt';

const emitter = mitt();

export default emitter;
Enter fullscreen mode Exit fullscreen mode

Step 3: Use the event bus in components

Component A – emitting an event:

<script setup>
import emitter from '../eventBus.js';

function sendMessage() {
  emitter.emit('custom-event', { text: 'Hello from Component A!' });
}
</script>

<template>
  <button @click="sendMessage">Send Event</button>
</template>
Enter fullscreen mode Exit fullscreen mode

Component B – listening for an event:

<script setup>
import { onMounted, onUnmounted } from 'vue';
import emitter from '../eventBus.js';

function handleMessage(payload) {
  console.log('Received:', payload.text);
}

onMounted(() => {
  emitter.on('custom-event', handleMessage);
});

onUnmounted(() => {
  emitter.off('custom-event', handleMessage);
});
</script>

<template>
  <p>Check the console for incoming messages 🚀</p>
</template>
Enter fullscreen mode Exit fullscreen mode

That’s it! You now have a simple global event bus in Vue 3.

✅ Best Practices

  • Use event bus for lightweight, decoupled communication — great for quick prototyping or small features.
  • Avoid overusing it — too many global events can make your app hard to debug.
  • Consider Pinia for complex apps — for apps with shared state or multiple event dependencies, Pinia is a more structured and scalable alternative.
  • Always clean up listeners — use onUnmounted to avoid memory leaks.

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

🧪 Advance skills

A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you're advancing your career or switching paths, it's a smart step toward success.

Check out Certificates.dev by clicking this link or by clicking the image below:

Certificates.dev Link

Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more!

✅ Summary

While Vue 3 removed the built-in event bus functionality, it doesn’t mean you can’t still use one. With a small library like mitt, you can easily create a global event bus for simple communication needs.

That said, if your application grows and requires shared state or complex communication, Pinia is the recommended solution for maintainability and scalability.

Take care!

And happy coding as always 🖥️

Top comments (1)

Collapse
 
kylelogue profile image
Kyle Logue

Have you considered a composable alternative that might complement your approach:

// useEventBus.js
import { ref } from 'vue'

const events = ref(new Map())

export function useEventBus() {
  const emit = (event, payload) => {
    const handlers = events.value.get(event) || []
    handlers.forEach(handler => handler(payload))
  }

  const on = (event, handler) => {
    const handlers = events.value.get(event) || []
    events.value.set(event, [...handlers, handler])
  }

  const off = (event, handler) => {
    const handlers = events.value.get(event) || []
    events.value.set(event, handlers.filter(h => h !== handler))
  }

  return { emit, on, off }
}
Enter fullscreen mode Exit fullscreen mode

What do you think?