If you are working with Odoo custom modules and want to improve the user interface, one of the most practical things you can do is add custom icons in the systray. In this guide, we will walk through how to add an icon in systray in Odoo 17 by creating a complete custom module, writing the necessary JavaScript (OWL), XML templates, and CSS, and then explaining the logic behind each part of the code.
This approach is developer-friendly, SEO-optimized, and follows the modern Odoo 17 web client architecture. By the end of this tutorial, you will have a fully functional system tray icon that displays dynamic data (meeting count, in our case) and opens the calendar when clicked.
Table of Contents
Step-by-Step Guide: How to Add an Icon in Systray in Odoo 17
Now let’s dive into the actual process. We’ll create a custom module that displays a new icon in the systray.
Step 1: Understanding the Goal
Before jumping into code, let’s clarify what we want to achieve:
- Add a custom icon in Odoo’s systray (top right corner of the web client).
- Fetch dynamic data using Odoo services (
orm
andaction
). - Display a badge counter (e.g., meeting count).
- On click, redirect the user to the calendar view.
- Make sure the module is clean, reusable, and developer-friendly.
This kind of feature is useful when you want quick access to notifications, pending tasks, or alerts directly from the top bar of Odoo.
Step 2: Create a Custom Module Structure
Let’s create a new custom module called calendar_systray_icon
. The basic module folder structure will look like this:
calendar_systray_icon/
│
│── __init__.py
│── static/
│ └── src/
│ ├── js/
│ │ └── calendar_systray.js
│ ├── xml/
│ │ └── calendar_systray.xml
│ └── css/
│ └── calendar_systray.scss
└── __manifest__.py
JSONStep 3: Define the Module Manifest
# How to Add an Icon in Systray in Odoo 17
{
"name": "Calendar Systray Icon",
"version": "17.0.1.0.0",
"summary": "Add a calendar icon in systray with meeting counter",
"description": """
This module demonstrates how to add an icon in systray in Odoo 17.
It shows the meeting count dynamically and redirects to calendar view.
""",
"category": "Tools",
"author": "Your Name",
"license": "LGPL-3",
"depends": ["web", "calendar"],
"assets": {
"web.assets_backend": [
"calendar_systray_icon/static/src/js/calendar_systray.js",
"calendar_systray_icon/static/src/xml/calendar_systray.xml",
"calendar_systray_icon/static/src/css/calendar_systray.scss",
],
},
"installable": True,
"application": False,
}
PythonStep 4: JavaScript (OWL Component)
In calendar_systray.js
, We’ll add the logic for the systray icon.
/** @odoo-module */
import { registry } from "@web/core/registry";
import { Component, useState, onWillStart } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class CalendarSystray extends Component {
static template = "calendar.CalendarSystray";
static props = {};
setup() {
this.action = useService("action");
this.orm = useService("orm");
this.state = useState({
meetingCount: 0,
loading: true,
});
onWillStart(async () => {
await this.loadMeetingCount();
});
}
async loadMeetingCount() {
try {
const count = await this.orm.call("res.users", "systray_get_activities", []);
const calendarGroup = count.find(group => group.type === "meeting");
this.state.meetingCount = calendarGroup ? calendarGroup.meetings.length : 0;
} catch (error) {
console.error("Error loading meeting count:", error);
this.state.meetingCount = 0;
} finally {
this.state.loading = false;
}
}
onClick() {
this.action.doAction("calendar.action_calendar_event", {
additionalContext: {
default_mode: "day",
search_default_mymeetings: 1,
},
clearBreadcrumbs: true,
});
}
}
registry.category("systray").add("calendar.CalendarSystray", {
Component: CalendarSystray,
isDisplayed: (env) => {
return env.services.user.hasGroup("base.group_user");
},
}, { sequence: 30 });
JavaScriptExplanation:
useService("orm")
: Calls Odoo models.useService("action")
: Triggers Odoo actions.onWillStart
: Lifecycle hook to load data before rendering.this.state
: Reactive state to hold meeting count.onClick
: Redirects to the calendar when the icon is clicked.registry.category("systray").addRegisters
the component in the system tray.
Step 5: XML Template
In calendar_systray.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="calendar.CalendarSystray" owl="1">
<div class="o_systray_item o_calendar_systray" role="button" t-on-click="onClick">
<div class="o_calendar_systray_icon">
<i class="fa fa-home" title="Calendar"/>
<span t-if="state.meetingCount > 0" class="o_calendar_systray_badge">
<t t-esc="state.meetingCount"/>
</span>
</div>
</div>
</t>
</templates>
XMLExplanation:
fa fa-home
: Font Awesome icon (you can replace it with any other).t-if
Ensures the badge only appears if the meeting count is greater than 0.t-esc
: Displays the meeting count dynamically.
Step 6: CSS Styling
In calendar_systray.scss
:
.o_calendar_systray {
cursor: pointer;
padding: 8px;
transition: background-color 0.2s ease;
position: relative;
&:hover {
background-color: rgba(0, 0, 0, 0.1);
}
.o_calendar_systray_icon {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
position: relative;
.fa-home {
font-size: 18px;
color: rgba(255, 255, 255, 0.9);
transition: color 0.2s ease;
}
.o_calendar_systray_badge {
position: absolute;
top: 2px;
right: 0;
background-color: #e74c3c;
color: white;
border-radius: 50%;
min-width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
line-height: 1;
padding: 0 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
}
&:hover .o_calendar_systray_icon .fa-home {
color: #ffffff;
}
}
CSSExplanation:
- Adds hover effect.
- Style the badge as a small red circle.
- Makes the icon visually appealing and noticeable.
Step 7: Installing the Module
- Place the module in your Odoo custom addons directory.
- Restart your Odoo server:
./odoo-bin -c odoo.conf -u calendar_systray_icon
- Activate developer mode.
- Install the module from the Apps menu.
Once installed, you will see a calendar icon with a counter in your Odoo systray.
Step 8: Testing the Feature

- Create some calendar events for the logged-in user.
- Refresh your Odoo instance.
- The systray icon should now display the number of meetings.
- Clicking on the icon should redirect you to the Calendar app with your meetings filtered.
Related Blogs:
>> How to Add a Custom Filter in Odoo
>> What is Odoo Runbot and How to Access It?
>> How to Add a Button to the Action Menu in Odoo 17
Final Thoughts
Adding systray icons in Odoo 17 is a great way to improve usability and provide quick access to important information. In this article, we built a complete custom module to demonstrate how to add an icon in systray in Odoo 17, including JavaScript logic, XML templates, CSS styling, and asset management.
By customizing this code, you can create systray icons for notifications, reminders, approvals, or any other real-time information your business needs.
If you’re an Odoo developer, this approach will become handy whenever clients ask for quick shortcuts or personalized notifications in the Odoo web client.