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
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;
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>
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>
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:
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:
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)
Have you considered a composable alternative that might complement your approach:
What do you think?