Welcome, Odoo enthusiasts and developers! Today, we’re diving deep into a truly powerful aspect of Odoo 18 customization: developing a custom Odoo 18 Systray Widget. If you’ve ever wished for quick access to critical functions, real-time alerts, or streamlined navigation directly from your Odoo backend, then mastering systray widgets is your key. This comprehensive guide will walk you through creating a custom systray widget using Odoo’s modern OWL framework, transforming your user experience and unlocking new levels of productivity.
This tutorial is based on a detailed document explaining the process of creating such a widget.
Why Every Odoo 18 User Needs a Custom Systray Widget
The systray, located in the top-right corner of the Odoo backend, is more than just a collection of icons; it’s a prime piece of digital real estate. It’s the perfect place to integrate tools and notifications that demand immediate attention or frequent access. A custom Odoo 18 Systray Widget offers unparalleled opportunities to:
- Boost Productivity: Imagine instantly seeing pending tasks, quickly creating a new sales order, or accessing a specific report without navigating through multiple menus.
- Enhance User Experience: Provide a more intuitive and responsive interface tailored to your business needs.
- Deliver Real-Time Information: Display live updates, system alerts, or critical KPIs at a glance.
- Streamline Workflows: Reduce clicks and improve efficiency by bringing essential actions to the forefront.
In a world where every second counts, a well-designed systray widget can dramatically improve the daily operations of your Odoo users.
Prerequisites for Building Your Odoo 18 Systray Widget
Before we jump into the code, ensure you have:
- Odoo 18 Instance: A running Odoo 18 development environment.
- Basic Odoo Development Knowledge: Familiarity with creating custom Odoo modules.
- JavaScript & XML Basics: Understanding of JavaScript, especially ES6 syntax, and XML for UI templating.
- OWL Framework Awareness: Odoo 18 heavily relies on OWL (Odoo Web Library) for its frontend. While we’ll explain the specifics, a general understanding of component-based frameworks will be beneficial.
Understanding Odoo’s Frontend Architecture and OWL
Odoo’s backend UI has evolved significantly, with OWL at its core since Odoo 14. OWL is a declarative, component-based JavaScript framework inspired by React and Vue.js. It simplifies building complex and interactive user interfaces by allowing you to break down your UI into reusable components.
When we create an Odoo 18 Systray Widget, we’re essentially building a new OWL component and registering it with Odoo’s frontend registry to appear in the systray area. This modern approach ensures better performance, maintainability, and a more robust development experience. For more detailed information on OWL, you can refer to the official OWL documentation (or the relevant Odoo documentation for your version, usually under “Developer -> Reference -> Backend -> OWL”).
What We’re Building: A Simple Alert Widget
For this tutorial, we will create a foundational Odoo 18 Systray Widget. This widget will display a simple Odoo icon in the systray. When clicked, a dropdown menu will appear, containing a single item labeled “Throw Alert”. Clicking this item will trigger a basic JavaScript alert box. This example, while simple, provides a solid foundation for more advanced functionalities you might implement.
Project Structure for Your Custom Odoo 18 Systray Widget
Every custom Odoo module requires a specific directory structure. Our module, named custom_systray_alert, will follow standard conventions:
custom_systray_alert/
├── static/
│ ├── src/
│ ├── js/
│ │ └── systray_alert.js
│ └── xml/
│ └── systray_alert.xml
└── __manifest__.py
Let’s break down why this structure is essential:
custom_systray_alert/: The root directory for your Odoo module.static/: This folder holds all static assets (JavaScript, CSS, XML templates, images, etc.) that the browser needs to load.src/: A common convention withinstatic/to organize source files.js/: Contains our JavaScript files, where we define the OWL component logic for our Odoo 18 Systray Widget.xml/: Houses our QWeb/OWL XML templates, which define the visual structure of the widget.__manifest__.py: This crucial file tells Odoo about your module, including its name, version, dependencies, and most importantly, which static assets to load.
Step-by-Step Tutorial: Developing Your Odoo 18 Systray Widget
Let’s begin coding!
Step 1: Create the JavaScript Component (custom_systray_alert/static/src/js/systray_alert.js)
This JavaScript file is the brain of your Odoo 18 Systray Widget. It defines the OWL component, handling its setup, any necessary services, and the logic that executes when a dropdown item is clicked.
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Component } from "@odoo/owl";
import { Dropdown } from '@web/core/dropdown/dropdown';
import { DropdownItem } from '@web/core/dropdown/dropdown_item';
class SystrayAlert extends Component {
// The setup method is called before the component is rendered.
// It's where you initialize state, define services, and perform other setup tasks.
setup() {
super.setup(...arguments);
// The useService hook provides access to Odoo's core services.
// In this case, we're preparing to use the 'action' service,
// which is commonly used to open Odoo views or modals.
// Although not strictly used for the 'alert' in this simple example,
// it demonstrates how you would access core functionalities for more complex widgets.
this.action = useService("action");
}
// This method is called when the "Throw Alert" dropdown item is clicked.
// In a real-world scenario, this could trigger a more complex Odoo action.
_openModal() {
alert("Throwing alert from systray");
}
}
// Associate the OWL component with its corresponding QWeb template.
// Odoo will look for a template named "systray_alert" to render this component.
SystrayAlert.template = "systray_alert";
// Declare which other OWL components are used within this component's template.
// This allows OWL to correctly render the Dropdown and DropdownItem structures.
SystrayAlert.components = { Dropdown, DropdownItem };
// Register our component in the systray menu.
// This is the most crucial step for integrating your widget into Odoo's UI.
export const systrayMenu = { Component: SystrayAlert };
// 'registry.category("systray")' targets the systray area.
// '.add("SystrayAlert", systrayMenu, { sequence: 1 })' registers our widget.
// "SystrayAlert" is a unique identifier.
// 'systrayMenu' is our component definition.
// 'sequence: 1' defines its position (lower numbers appear first).
registry.category("systray").add("SystrayAlert", systrayMenu, { sequence: 1 });
Code Breakdown and Core Concepts:
/** @odoo-module **/: This directive tells Odoo’s module loader that this is an Odoo module, essential for its proper functioning.import { registry } from "@web/core/registry";: Theregistryis a central place in Odoo’s frontend where different UI components and services are registered. We use it to register ourOdoo 18 Systray Widget.import { useService } from "@web/core/utils/hooks";:useServiceis an OWL hook that allows you to easily access Odoo’s core services (likeaction,notification,http, etc.) within your components. This is fundamental for making your widget interactive with the Odoo backend.import { Component } from "@odoo/owl";: This imports the baseComponentclass from OWL, which all your custom UI elements will extend.import { Dropdown, DropdownItem } from '@web/core/dropdown/dropdown';: These imports bring in pre-built OWL components for creating dropdown menus, saving you from writing all the HTML and JavaScript for a responsive dropdown from scratch. This illustrates Odoo’s component-driven design.class SystrayAlert extends Component { ... }: This is where our customSystrayAlertcomponent is defined, inheriting all the capabilities of a standard OWLComponent.setup(): This lifecycle hook is where you typically initialize your component’s state and connect to services. Here,this.action = useService("action");prepares the component to use Odoo’s action service, which is critical for opening modals, navigating to different views, or executing server actions in more advanced scenarios._openModal(): This is a custom method defined within our component. It will be triggered when the corresponding UI element in our XML template is clicked. For now, it simply shows a JavaScript alert.SystrayAlert.template = "systray_alert";: This line links our JavaScript component to its visual representation defined in an XML template. Odoo will look for an XML template witht-name="systray_alert".SystrayAlert.components = { Dropdown, DropdownItem };: This declares that ourSystrayAlertcomponent will renderDropdownandDropdownItemcomponents within its template. This allows OWL to manage their lifecycle and rendering correctly.registry.category("systray").add("SystrayAlert", systrayMenu, { sequence: 1 });: This is the most critical line. It registers ourSystrayAlertcomponent with thesystraycategory of Odoo’s frontend registry. Thesequence: 1determines its order among other systray items; a lower number means it appears earlier. This ensures our custom Odoo 18 Systray Widget is visible in the Odoo backend.
Step 2: Create the XML Template (custom_systray_alert/static/src/xml/systray_alert.xml)
This XML file uses QWeb (Odoo’s templating engine) combined with OWL directives to define the visual structure and layout of your Odoo 18 Systray Widget, including its icon and the content of its dropdown menu.
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<!-- This template defines the structure for our SystrayAlert component. -->
<t t-name="systray_alert" owl="1">
<!-- The Dropdown component provides the overall structure for a clickable systray item
that reveals a dropdown menu. -->
<Dropdown>
<!-- The 'toggler' slot defines what appears directly in the systray.
This is the clickable part that opens the dropdown. -->
<t t-set-slot="toggler">
<div class="alert_systray">
<div>
<!-- This div acts as the clickable icon.
The 'oi oi-odoo' classes render the standard Odoo icon from Odoo's icon font. -->
<div class="toggle-icon" role="button">
<i id='create_order' class="oi oi-odoo"/>
</div>
</div>
</div>
</t>
<!-- The 'default' slot defines the content that appears inside the dropdown menu
when the toggler is clicked. -->
<t t-set-slot="default">
<!-- Each DropdownItem represents a single selectable item within the dropdown. -->
<DropdownItem>
<!-- This div contains the text for the dropdown item.
The 't-on-click' attribute binds the '_openModal' method from our
JavaScript component (SystrayAlert) to a click event on this div. -->
<div t-on-click="_openModal">
Throw Alert
</div>
</DropdownItem>
</t>
</Dropdown>
</t>
</templates>
Template Breakdown:
<t t-name="systray_alert" owl="1">: This line defines an OWL-compatible QWeb template namedsystray_alert, which we linked in our JavaScript. Theowl="1"attribute signifies it’s an OWL template.<Dropdown>: This is the OWL component we imported in our JavaScript. It intelligently handles the display and toggle logic for the dropdown.<t t-set-slot="toggler">: This specific slot is where you define the content that will be displayed directly in the systray itself. In our case, it’s a clickabledivcontaining an Odoo icon.<i class="oi oi-odoo"/>: This uses Odoo’s built-in icon font (Open Iconic) to display a generic Odoo logo. You could easily replace this with any other Odoo icon (oi-plus,oi-bell, etc.) or even a custom image if needed.
<t t-set-slot="default">: This slot defines the actual content that appears when the systray icon (the toggler) is clicked, forming the dropdown menu.<DropdownItem>: Another OWL component, this provides the correct styling and behavior for individual items within a dropdown.<div t-on-click="_openModal"> Throw Alert </div>: This is our clickable dropdown item. Thet-on-clickdirective is a QWeb/OWL way to bind a JavaScript method (in this case,_openModalfrom ourSystrayAlertcomponent) to a click event on thisdiv.
Step 3: Update the Manifest File (custom_systray_alert/__manifest__.py)
The __manifest__.py file acts as your module’s identity card. You must declare all your custom JavaScript and XML assets here so that Odoo knows to load them when your module is installed and active.
{
'name': "Custom Systray Alert",
'version': '1.0',
'depends': ['base', 'web'], # Essential dependencies for frontend development
'assets': {
'web.assets_backend': [ # Declare assets for the Odoo backend
'custom_systray_alert/static/src/js/systray_alert.js',
'custom_systray_alert/static/src/xml/systray_alert.xml',
],
},
'installable': True, # Marks the module as installable
'auto_install': False, # Set to True if this module should auto-install with dependencies
'application': False, # Set to True if this module should appear in the 'Apps' menu
}
Manifest File Breakdown:
'name': "Custom Systray Alert": The user-friendly name of your module.'version': '1.0': The version number of your module.'depends': ['base', 'web']: This is critical.'base'is the foundational Odoo module.'web'provides all the core frontend assets, including OWL, the registry, and basic UI components necessary for yourOdoo 18 Systray Widgetto function.
'assets': { ... }: This dictionary specifies which static files Odoo should load.'web.assets_backend': This is the key bundle where you should list all JavaScript and XML files that are meant to be loaded in the Odoo backend.- The paths to our
systray_alert.jsandsystray_alert.xmlfiles are listed here, telling Odoo to include them when building the backend assets.
'installable': True: This flag is typically set toTruefor most custom modules.'auto_install': False: If set toTrue, the module would automatically install if its dependencies are installed. For a standalone feature like this,Falseis usually appropriate.'application': False: IfTrue, your module would appear as an installable app in the Odoo “Apps” menu. For a backend extension like a systray widget,Falseis usually correct as it’s not a standalone application.
Step 4: Install and Test Your Odoo 18 Systray Widget
You’re almost there! Now, let’s get your new widget running in Odoo.
- Place the Module: Copy your entire
custom_systray_alertdirectory into your Odooaddonspath. If you’re unsure where this is, it’s typically a directory configured in your Odoo server’s startup parameters (--addons-path). - Restart Odoo: It’s a good practice to restart your Odoo server after adding new modules or changing manifest files.
- Update Applications List: In your Odoo backend, go to
Apps(ensure “Developer mode” is activated). Click on “Update Apps List”. This makes Odoo aware of your new module. - Install the Module: Search for “Custom Systray Alert” in the Apps list. Click “Install”.
- Test It Out!
- After installation, refresh your browser (Ctrl+F5 or Cmd+R) to clear the cache and load the new assets.
- You should now see a new Odoo icon in the top-right systray area, typically on the far left of the default systray icons (due to
sequence: 1). - Click on the Odoo icon. A dropdown menu should appear with “Throw Alert”.
- Click “Throw Alert”. A JavaScript alert box saying “Throwing alert from systray” should pop up!
Congratulations! You’ve successfully created and deployed your first custom Odoo 18 Systray Widget.
Beyond the Basics: Enhancing Your Odoo 18 Systray Widget
While our alert widget is a great starting point, the true power of an Odoo 18 Systray Widget lies in its potential for real-world applications. Here are some ideas for taking your widget to the next level:
- Opening Custom Modal Forms: Instead of an alert, use the
actionservice (this.action.doAction(...)) to open a pre-defined Odoo wizard or modal view for quick data entry (e.g., “Quick Create Sales Order,” “Log Time Entry”). - Displaying Real-Time Notifications: Fetch data from the backend using Odoo’s RPC mechanism (
this.env.services.rpc("/your/custom/route")) and display dynamic counts (e.g., “5 Pending Tasks,” “3 New Messages”) or use Odoo’s native notification service (this.env.services.notification.addNotification(...)). - Redirecting Users to Specific Views: Programmatically navigate users to a specific list view, form view, or dashboard within Odoo.
- Fetching Dynamic Data from the Backend: Integrate with Python methods on your Odoo models to retrieve and display live data. For instance, a widget showing today’s sales summary or overdue invoices.
- Integrating Third-Party Services: Display status updates from external systems (e.g., a “Service Status” icon that changes color based on an API call).
- User-Specific Customization: Tailor the widget’s content or visibility based on the logged-in user’s roles or preferences.
- Adding Dynamic Styling or Icons: Change the icon or apply conditional styling based on the widget’s state or fetched data.
For more advanced integrations and to learn about RPC calls, consider exploring the Odoo developer documentation on web services.
Troubleshooting Common Issues
If your widget isn’t appearing or working as expected, consider these common troubleshooting steps:
- Browser Cache: Always perform a hard refresh (Ctrl+F5 / Cmd+Shift+R) after installing/upgrading modules in Odoo, as browser caches can prevent new JavaScript/XML files from loading.
- Odoo Server Logs: Check your Odoo server logs for any errors during module installation or when loading assets.
- Browser Developer Console: Open your browser’s developer tools (F12) and check the “Console” tab for JavaScript errors and the “Network” tab to ensure your
systray_alert.jsandsystray_alert.xmlfiles are being loaded. - Manifest Errors: Double-check your
__manifest__.pyfor typos, especially in theassetssection. Incorrect paths will prevent files from loading. - Module Dependencies: Ensure
'web'is correctly listed in yourdependslist. - OWL/QWeb Syntax: Small typos in XML (
t-name,t-set-slot) or JavaScript (import,registry.category) can cause silent failures or render issues.
Conclusion: Unleash the Full Potential of Odoo 18
Mastering the Odoo 18 Systray Widget empowers you to create a more dynamic, user-friendly, and efficient Odoo experience. By following this step-by-step tutorial, you’ve not only built a functional widget but also gained valuable insights into Odoo’s modern frontend development using OWL. The ability to customize the systray opens up a world of possibilities for tailoring Odoo to your exact business processes, reducing friction, and boosting overall productivity.
Don’t stop here! Take this foundational knowledge and experiment with the advanced applications discussed. The Odoo community is vibrant, and the flexibility of the platform, especially with OWL, provides developers with incredible tools to innovate. Start building your next game-changing Odoo 18 Systray Widget today and truly transform your Odoo backend!
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.

