How to Use the useBus Hook to Communicate Between OWL Components in Odoo18
If you’ve spent any time building with Odoo’s OWL framework, you’ve probably hit a wall. You’ve got two awesome components, but they live in different parts of the UI.How can you maintain communication between your components without your code becoming completely disorganised? Allow me to present to you the useBus hook in Odoo18, a lifesaver. Since it’s more of a secret weapon for creating apps where components can communicate without being hardwired together, calling it a “utility” feels like a shorthand for what it is Let’s go over how it works with a quick example that you can use right now.
So, What’s the Big Deal with useBus Hook in Odoo18?
Picture it like a radio station for your app. One component can grab the microphone and broadcast a message (an “event”) on a specific frequency (the “bus”). Any other component, no matter where it is, can tune into that frequency and react when it hears the message.
What makes this so clever is how it handles the boring stuff for you. It sets up the listener when a component loads and—this is the crucial part—it cleans up after itself, removing the listener when the component is gone. That means no more memory leaks from forgotten, ‘zombie’ listeners causing chaos. It gives your components the freedom to interact without getting tangled up in messy parent-child chains.
The Syntax, Made Simple
Getting this up and running is pretty straightforward. First, you just need to pull it into your file:
| import { useBus } from “@web/core/utils/hooks”; |
You then make the call right inside your component’s setup method, like this:
| useBus(bus, eventName, callback); |
Let’s quickly translate that:
- bus: Consider “this.env.bus” to be your own Odoo radio channel. You will mostly utilise it to transmit and receive real-time communications throughout the system.
- eventName: Think of this as the secret code for your message, like “new-order-received” or “cart-updated”. It’s how your components know what to listen for.
- callback: This is just the function that fires off when the event comes in—your code, ready to roll.
The useBus hook in Odoo18 handles all the behind-the-scenes setup and cleanup, so you can focus on what you want to happen without sweating the details.
Let’s Build Something: A Simple Counter App
Theory is one thing, but code is another. Let’s create a simple counter with two separate components in the systray to see how this works in practice.
- The CounterView: Its only job is to show a number.
- The Counter: It just has a button that, when clicked, needs to tell the CounterView to update.
They don’t know anything about each other. Their only connection is the event bus.
The JavaScript Logic
Here’s the code that brings it to life.
| /** @odoo-module **/ import { registry } from “@web/core/registry”; import { Component, useState } from “@odoo/owl”; import { useBus } from “@web/core/utils/hooks”; // Component 1: The Display (Listens for events) export class CounterView extends Component { static template = “owl_bus_service.CounterView”; setup() { this.state = useState({ value: 0 }); // Here’s the magic: we’re tuning into the “increment” frequency useBus(this.env.bus, “increment”, () => this.increment()); } increment() { this.state.value++; } } // Component 2: The Button (Broadcasts the event) export class Counter extends Component { static template = “owl_bus_service.Counter”; increment() { // Shouting “increment” out to anyone who’s listening this.env.bus.trigger(“increment”); } } // — Registering both components to the systray — registry.category(“systray”).add(“my_app.counter_view”, { Component: CounterView }, { sequence: 10 }); registry.category(“systray”).add(“my_app.counter_button”, { Component: Counter }, { sequence: 20 }); |
The XML Templates
And here are the templates that give our components a face.
| <?xml version=”1.0″ encoding=”UTF-8″?> <template xml:space=”preserve”> <!– The Display Component’s Template –> <t t-name=”owl_bus_service.CounterView”> <div class=”o_systray_item”> <span class=”fw-bold”>Count: </span> <span t-esc=”state.value” /> </div> </t> <!– The Button Component’s Template –> <t t-name=”owl_bus_service.Counter”> <div class=”o_systray_item”> <button class=”btn btn-sm btn-secondary” t-on-click=”increment”> Increment </button> </div> </t> </template> |
So what happens when someone clicks that button?
- The click on the Counter component’s button runs its increment method.
- That method calls this.env.bus.trigger(“increment”), effectively broadcasting the message.
- Over in the CounterView, the useBus hook that’s been listening since setup immediately catches the “increment” signal.
- It runs the callback function we gave it, which in turn calls the increment method inside CounterView.
- When you bump up state.value by one, the useState hook tells OWL to automatically refresh the component, so the new number pops up on the screen without any extra work
Why You Need This in Your Toolbox
- It keeps things tidy: Your components don’t have to know each other exists, which makes your code way easier to manage, test, and tweak down the road.
- Perfect for app-wide updates: It’s a lifesaver when one action needs to light up multiple parts of your UI—like a notification bell, a status bar, and a dashboard all updating the moment a new message rolls in.
- Avoids “prop drilling”: You can skip the headache of passing data down through multiple layers of components that don’t even need it.
A Few Tips and Traps to Avoid
Before you go all-in, keep these pointers in mind to stay out of trouble:
- Be Specific with Event Names. Don’t use a generic name like save. Go for something unique like my_module.form_saved to prevent strange bugs from other apps listening to the same thing.
- Don’t Abuse It. The event bus is for broadcasting. If two components are really a single unit (like a list and its items), passing data with props is still the cleaner, more direct way to go.
- Beware of Silent Errors. If your callback function has a bug, the event will fire but your UI just won’t update. Keep your browser’s developer console open while you’re working to catch these.
- Manual Listeners are Risky. If you ever step outside useBus and set up a listener with bus.on(), it’s on you to tear it down with bus.off() when the component unmounts. You just made a memory leak if you forget. Just use useBus hook in Odoo18 —it does it for you.
"Automate Your Business with our Customized Odoo ERP Solutions"
"Get a Cost Estimate for Your ERP Project, Absolutely FREE!"
Get a Free Quote



