Odoo 18 continues to build on its robust front-end architecture powered by OWL (Odoo Web Library), a modern, reactive framework written in JavaScript. One of the standout features for developers working with OWL components is the ability to handle event-driven communication between components using hooks like useBus.
In this blog post, we’ll explore the useBus hook in depth, what it is, how it works, its syntax, and how you can apply it to create dynamic, decoupled components in your Odoo applications. We'll walk through a real-world example that demonstrates how one component can update another seamlessly using OWL and useBus.
What is useBus?
The useBus hook is a built-in OWL utility available in Odoo that allows components to listen for events emitted on an event bus. It is part of the @web/core/utils/hooks module and is typically used to handle cross-component communication without direct parent-child relationships.
The beauty of useBus is that it:
* Adds an event listener to a bus during component setup.
* Automatically removes the listener when the component is unmounted.
* Helps prevent memory leaks or unintended behavior from lingering event handlers.
It is especially useful when you want multiple components to react to the same global event, something you can’t easily achieve through props or state sharing.
Syntax:
import { useBus } from "@web/core/utils/hooks";
useBus(bus, eventName, callback);
* bus: The event bus instance (commonly this.env.bus in Odoo).
* eventName: A string identifier for the event you're listening to.
* callback: The function to be executed when the event occurs.
The hook registers the listener during component setup and unregisters it on teardown, keeping your code clean and memory-efficient.
A Practical Example
Let’s look at a real-world example: a counter app consisting of two components.
* CounterView: Displays the current count.
* Counter: Contains a button that emits the "increment" event.
When the button is clicked in the Counter component, the CounterView listens to the "increment" event and updates its display accordingly.
JavaScript Code:
JavaScript (Component Logic)
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component, useState } from "@odoo/owl";
import { useBus } from "@web/core/utils/hooks";
// CounterView: Displays the current counter value and listens to "increment" events
export class CounterView extends Component {
static template = "owl_bus_service.CounterView";
setup() {
this.state = useState({ value: 0 });
useBus(this.env.bus, "increment", () => this.increment());
}
increment() {
this.state.value++;
}
}
const systrayItemCounterView = {
Component: CounterView,
};
registry.category("systray").add("counter_view_systray", systrayItemCounterView, { sequence: 0 });
// Counter: Emits the "increment" event when clicked
export class Counter extends Component {
static template = "owl_bus_service.Counter";
increment() {
this.env.bus.trigger("increment");
}
}
const systrayItem = {
Component: Counter,
};
registry.category("systray").add("counter_systray", systrayItem, { sequence: 0 });
XML Template Code:
<?xml version="1.0" encoding="UTF-8"?>
<template xml:space="preserve">
<!-- Display Component -->
<t t-name="owl_bus_service.CounterView">
<div class="btn" style="background-color: white; color: black; border: 1px solid #ccc;padding-bottom: 25px;">
Counter: <t t-esc="state.value" />
</div>
</t>
<!-- Button Component -->
<t t-name="owl_bus_service.Counter">
<div>
<button
class="btn"
style="background-color: white; color: black; border: 1px solid #ccc;"
t-on-click="increment">Increment
</button>
</div>
</t>
</template>
How It Works
Here’s what happens step by step:
1. CounterView component is mounted and sets up a listener for "increment" using useBus.
2. Counter component has a button that triggers the "increment" event using this.env.bus.trigger("increment").
3. When clicked, the event is emitted globally.
4. CounterView, upon detecting the event, invokes its increment() method.
5. The state is updated using useState, and the component re-renders automatically.
Result
With this setup:
* Clicking the "Increment" button in the Counter component triggers an event.
* The CounterView component updates and reflects the new value, even though the two components are unrelated in the DOM.
Why Use useBus?
* Loose coupling: Components don’t need to be related hierarchically to communicate.
* Centralized events: Ideal for triggering global UI updates (notifications, status changes, counters).
* Memory safety: Listeners are cleaned up automatically.
* Cleaner code: Avoids deeply nested prop drilling or complex state sharing.
Use Cases in Existing Odoo Apps
The useBus hook isn’t limited to toy examples. Here are some practical use cases:
1. Notification Bubbles
A notification component listens to "new-message" events and updates a bubble in the header.
2. Status Broadcasts
When a background process completes, it emits an event like "sync-complete". Multiple components listening can refresh or update accordingly.
3. User Presence
Track online users by broadcasting "user-online" or "user-offline" events.
4. Live Dashboard Updates
Push real-time sales updates to dashboard widgets without reloading the whole view.
Best Practices
* Use unique and descriptive event names to avoid conflicts.
* Do not abuse the event bus for tightly coupled interactions — prefer props or shared stores when applicable.
* Avoid emitting events too frequently; batch updates if possible to reduce noise.
Common Pitfalls
* Missing teardown: If you manually add listeners outside useBus, you must also clean them up — or you risk memory leaks.
* Incorrect event names: Double-check your event name string for typos or mismatches.
* Silent errors: If your callback throws an error, it might silently fail without any UI feedback — use console.error or error boundaries when debugging.
Conclusion
The useBus hook is a hidden gem in OWL and Odoo’s modern frontend architecture. It enables developers to create modular, reactive, and loosely-coupled components that scale well and are easy to maintain. By understanding and applying useBus, you can build more flexible and dynamic interfaces, whether it's updating a single counter or orchestrating a suite of dashboard widgets in real time. If you're building or maintaining OWL-based UIs in Odoo 18, mastering useBus is an essential step toward clean, efficient, and modern frontend code.
To read more about Overview of Owl Hooks in Odoo 18, refer to our blog Overview of Owl Hooks in Odoo 18.