Create Custom Hooks in Odoo 18 OWL
By Braincuber Team
Published on February 4, 2026
Hooks in Odoo's OWL (Odoo Web Library) framework are a powerful way to encapsulate and reuse stateful logic across components. Just like React hooks, they allow you to separate business logic from UI components, making your code cleaner, testable, and more modular.
In this tutorial, we will create a custom Network Status Hook. This hook will track the user's online/offline status in real-time and display a reactive indicator in the Odoo Systray (System Tray). This provides immediate visual feedback when an internet connection is lost—critical for POS or warehouse operations.
Encapsulation
Bundle state and side effects (like event listeners) into a single reusable function.
Reactivity
Leverage OWL's useState to automatically trigger UI updates when data changes.
Lifecycle Management
Automatically clean up event listeners with onWillDestroy to prevent memory leaks.
1. Implementation Structure
We will create a module named web_network_status with the following structure:
web_network_status/
├── __init__.py
├── __manifest__.py
└── static/
└── src/
├── hooks/
│ └── use_network_status.js
└── components/
├── network_indicator.js
└── network_indicator.xml
2. Creating the Custom Hook
Our hook useNetworkStatus will manage the online state. It initializes with the current status and updates whenever the browser fires online or offline events.
/** @odoo-module **/
import { useState, onWillDestroy } from "@odoo/owl";
/**
* Custom hook to track network connectivity status
* @returns {Object} Reactive state containing booleans { isOnline, isOffline }
*/
export function useNetworkStatus() {
// 1. Initialize reactive state
const status = useState({
isOnline: navigator.onLine,
isOffline: !navigator.onLine
});
// 2. Define event handlers
const updateOnline = () => {
status.isOnline = true;
status.isOffline = false;
};
const updateOffline = () => {
status.isOnline = false;
status.isOffline = true;
};
// 3. Attach listeners
window.addEventListener("online", updateOnline);
window.addEventListener("offline", updateOffline);
// 4. Cleanup on component destruction
onWillDestroy(() => {
window.removeEventListener("online", updateOnline);
window.removeEventListener("offline", updateOffline);
});
return status;
}
Best Practice: Always use onWillDestroy inside your hooks to remove event listeners. This ensures that when the component using the hook is removed from the DOM, the listeners don't persist (which would cause memory leaks).
3. Building the Systray Component
Now we create a small component that uses our hook and displays a badge in the system tray.
/** @odoo-module **/
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { useNetworkStatus } from "../hooks/use_network_status";
class NetworkIndicator extends Component {
static template = "web_network_status.NetworkIndicator";
setup() {
// Initialize our custom hook
this.network = useNetworkStatus();
}
}
// Add to the Systray (System Tray)
registry.category("systray").add("network_indicator", {
Component: NetworkIndicator,
sequence: 1, // High priority to show on the left
});
4. Component Template
The XML template defines how the indicator looks. We'll use Bootstrap utilities for styling.
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="web_network_status.NetworkIndicator" owl="1">
<div class="o_network_indicator d-flex align-items-center px-2"
t-if="network.isOffline"
title="No Internet Connection">
<!-- Offline Badge -->
<span class="badge rounded-pill bg-danger d-flex align-items-center gap-1">
<i class="fa fa-wifi text-white"/>
<span class="d-none d-md-inline">OFFLINE</span>
</span>
</div>
</t>
</templates>
Note: We used t-if="network.isOffline" to only render the component when the user is offline. When online, it stays invisible to keep the interface clean.
5. Module Manifest
Finally, register the assets in the manifest file.
{
'name': 'Odoo Network Status Hook',
'version': '18.0.1.0.0',
'category': 'Technical',
'summary': 'OWL Hook to track online/offline status',
'depends': ['web'],
'assets': {
'web.assets_backend': [
'web_network_status/static/src/hooks/*.js',
'web_network_status/static/src/components/*.js',
'web_network_status/static/src/components/*.xml',
],
},
'installable': True,
}
Why Use Custom Hooks?
- Code Reusability: Use the same logic (e.g., fetching data, tracking events) in multiple components without copy-pasting.
- Separation of Concerns: Components focus on rendering; Hooks focus on state and logic.
- Simplification: Removes clutter from
setup()methods in complex components.
