In Vue 2, developers often used an event bus (a new Vue instance) to send and listen for events across components. However, this pattern was removed in Vue 3 to encourage clearer, more maintainable communication.
Let’s see how to safely implement an event bus in Vue 3 using the Composition API.
Creating a Vue 3 Event Bus with mitt
The most recommended way to create an event bus in Vue 3 is by using mitt, a tiny functional event emitter.
Step 1 – Install mitt
npm install mitt
Step 2 – Create the Event Bus
// eventBus.jsimport mitt from 'mitt'
const emitter = mitt()
export default emitter
Step 3 – Register it globally
import { createApp } from 'vue'import App from './App.vue'
import emitter from './eventBus'
const app = createApp(App)
app.config.globalProperties.emitter = emitter
app.mount('#app')
Step 4 – Emit and listen to events in components:
<!-- ComponentA.vue -->export default {
mounted() {
this.emitter.emit('custom-event', 'Hello from A')
}
}
<!-- ComponentB.vue -->export default {
mounted() {
this.emitter.on('custom-event', (msg) => {
console.log('Received:', msg)
})
}
}
Alternative Approaches
Here are the three alternatives:
1. Props and Emits
Use props to send data from parent to child and emits for the reverse.
2. Provide / Inject
Scoped injection of values without prop drilling.
// Parentapp.provide('theme', 'dark')
// Child
const theme = inject('theme')
3. Pinia or Vuex
Best for app-wide state and reactive event-driven logic.
Also Read: The Vue JS Advantages In Detail
Best Practices
- Avoid global event spam – only use for cross-cutting concerns
- Always clean up listeners – use onUnmounted()
- Prefer props/events or stores for tightly-coupled or data-rich flows
- Use descriptive event names – like 'cart:add-item' or 'notification:open'
Key Takeaway
Vue 3 removed the native event bus to improve app structure, but using a library like mitt lets you implement a clean, performant alternative. Keep events scoped and manageable, and always unsubscribe to avoid leaks.
Related Resources