Odoo keeps pushing the limits of contemporary ERP web development with every release, and Odoo 19 is the next significant advancement in frontend technology. Odoo 19 continues to improve the Owl (Odoo Web Library) framework as the cornerstone of its web client architecture, building on the strong foundations established in Odoo 17 and further improved in Odoo 18.
Owl in Odoo 19 further incorporates reactive design principles and contemporary JavaScript standards, allowing developers to create extremely dynamic, efficient, and maintainable user interfaces. Its component-based architecture is still essential, enabling the development of UI elements as reusable, modular parts that easily interface with Odoo's service layer and backend. This method keeps codebases clean, scalable, and simpler to maintain while greatly increasing development speed.
Odoo 19 frees developers to concentrate on providing useful functionality rather than managing UI complexity by further improving OWL's performance and simplicity. Odoo's position as a cutting-edge, web-first ERP platform prepared for the upcoming generation of business applications is strengthened by the ensuing smoother, quicker, and more engaging user experience.
Before diving into more complex applications, let's start by creating a simple Owl component in Odoo.
Javascript:
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component, useState } from "@odoo/owl";
export class Counter extends Component {
static template = "my_module.Counter";
setup() {
this.state = useState({
counter: 10,
});
}
increment() {
this.state.counter++;
console.log(this.state.counter);
}
}
registry.category("actions").add("Counter", Counter);
XML:
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="my_module.Counter">
<h1>Counter</h1>
<p>Counter: <t t-esc="state.counter"/></p>
<button
class="btn btn-primary"
t-on-click="increment">
Increment
</button>
</t>
</templates>
This example shows that Owl is available as a library in the global namespace as owl; it can simply be used like most libraries in Odoo. This example creates a simple counter app that increments its value with each button click.
The Odoo JavaScript framework includes a collection of reusable UI components designed for common use cases, such as dropdown menus, checkboxes, and date selectors. This guide describes how to implement and work with these standard components in your projects.
| Technical Name | Short Description |
| ActionSwiper | a swiper component to perform actions on touch swipe |
| CheckBox | a simple checkbox component with a label next to it |
| ColorList | a list of colors to choose from |
| Dropdown | full-featured dropdown |
| Notebook | a component to navigate between pages using tabs |
| Pager | a small component to handle pagination |
| SelectMenu | a dropdown component to choose between different options |
| TagsList | a list of tags displayed in rounded pills |
ActionSwiper:
Location: @web/core/action_swiper/action_swiper
The ActionSwiper component enables actions to be triggered through horizontal swipe gestures. It wraps a target element and monitors user interaction, executing the associated action once the element is swiped beyond a defined portion of its width and released.
<ActionSwiper onLeftSwipe="Object" onRightSwipe="Object">
<SomeElement/>
</ActionSwiper>
Use this component by wrapping it directly in the XML template around your target element. This method makes it simple to incorporate swipe functionality without having to duplicate or alter pre-existing templates. You can easily extend an existing element's behaviour by placing it inside the ActionSwiper. You can also use props to manage animations, specify the minimum swipe distance needed to initiate an action, and control when the element is swipeable.
In this example, we use both onLeftSwipe and onRightSwipe props for the ActionSwiper component. Here are the props available for ActionSwiper:
| Name | Type | Description |
| animationOnMove | boolean | optional boolean to determine if a translate effect is present during the swipe |
| animationType | String | optional animation that is used after the swipe ends (bounce or forwards) |
| onLeftSwipe | Object | if present, the actionswiper can be swiped to the left |
| onRightSwipe | Object | if present, the actionswiper can be swiped to the right |
| swipeDistanceRatio | number | optional minimum width ratio that must be swiped to perform the action |
CheckBox:
Location: @web/core/checkbox/checkbox
This component features a basic checkbox accompanied by a label. The two are linked together, allowing the checkbox to be toggled easily by clicking on the label.
<CheckBox value="boolean" disabled="boolean" t-on-change="onValueChange">
Some Text
</CheckBox>
Available props for CheckBox are:
| Name | Type | Description |
| value | boolean | if true, the checkbox is checked, otherwise it is unchecked |
| disabled | boolean | if true, the checkbox is disabled, otherwise it is enabled |
ColorList:
Location: @web/core/colorlist/colorlist
The ColorList component allows users to select a color from a predefined set. By default, it displays the currently selected color and remains collapsed unless the canToggle prop is enabled. Its behavior can be customized through props to either keep the list always expanded or allow it to toggle open on click, showing the available colors until a selection is made.
Available props for ColorList are:
| Name | Type | Description |
| canToggle | boolean | optional. Whether the colorlist can expand the list on click |
| colors | array | list of colors to display in the component. Each color has a unique id |
| forceExpanded | boolean | optional. If true, the list is always expanded |
| isExpanded | boolean | optional. If true, the list is expanded by default |
| onColorSelected | function | callback executed once a color is selected |
| selectedColor | number | optional. The color id that is selected |
Color id’s are the following:
| Id | Color |
| 0 | No color |
| 1 | Red |
| 2 | Orange |
| 3 | Yellow |
| 4 | Light Blue |
| 5 | Dark Purple |
| 6 | Salmon Pink |
| 7 | Medium Blue |
| 8 | Dark Blue |
| 9 | Fuchsia |
| 12 | Green |
| 11 | Purple |
DropDown:
Location: @web/core/dropdown/dropdown and @web/core/dropdown/dropdown_item
The Dropdown component displays a menu containing a list of items when a toggle is clicked. It can be combined with DropdownItem elements to trigger callbacks and automatically close the menu upon selection, providing a flexible and user-friendly interaction experience.
Features provided are:
- Toggle the item list on click
- Close the dropdown on outside click
- Trigger a function when an item is selected
- Optionally close the list after item selection
- Style It Yourself (customizable styling)
- Support nested sub-dropdowns at any level
- Configurable hotkeys to open, close, or select dropdown items
- Full keyboard navigation (arrows, Tab, Shift+Tab, Home, End, Enter, Escape)
- Automatically reposition on page scroll or resize
- Smartly determine opening direction (including right-to-left layouts)
- Manage direct sibling dropdowns: when one is open, others toggle on hover
To use the <Dropdown/> component correctly, you must define two OWL slots. The default slot contains the toggle element of the dropdown, where click events are automatically bound to open and close the menu. The content slot holds the dropdown menu elements and is rendered inside a popover; it can include DropdownItem components, which automatically close the dropdown when selected. To support this structure, Odoo provides two core components: the Dropdown component, which manages the dropdown behavior, and the DropdownItem component, which represents individual items within the menu.
<Dropdown>
<button class="my-btn" type="button">
Click me to toggle the dropdown menu!
</button>
<t t-set-slot="content">
<DropdownItem onSelected="selectItem1">Menu Item 1</DropdownItem>
<DropdownItem onSelected="selectItem2">Menu Item 2</DropdownItem>
</t>
</Dropdown>
Available Dropdown properties are:
| Name | Type | Description |
| menuClass | string | Optional classname added to the dropdown’s menu |
| disabled | boolean | Optional, if true, disables the dropdown so the user is not able to open it anymore. (default: false) |
| items | Array | Optional list of items to be displayed as DropdownItems insusePositionide the dropdown’s menu |
| position | string | Optionally defines the desired menu opening position. RTL direction is automatically applied. Should be a valid usePosition hook position. (default: bottom-start) |
| beforeOpen | Function | Optional function called just before opening. May be asynchronous. |
| onOpened | Function | Optional function called just after opening. |
| onStageChanged | Function | Optional function called after opening or closing (gives a boolean as a single argument that represents whether the dropdown is open or not). |
| state | Object | Optional object with open(), close() and isOpen properties to manually control when the dropdown opens and closes. |
| manual |
| Optional, when true, the Dropdown component will not add click event listeners to the toggler. This allows for more control as when to open the dropdown. (This should be used in tandem with the state prop) |
| navigationOptions | Boolean | Optionally overrides the navigation options of the dropdown, (see web/core/navigation/navigation) |
| holdOnHover | Boolean | Optional, if true, keeps the Dropdown’s menu at the same position while the mouse is hovering it, creating a better UX when the menu’s content changes. |
| menuRef | Function | Optional, allows to get a ref of the dropdown’s menu, (expects a function returned from useChildRef) |
NoteBook:
Location: @web/core/notebook/notebook
The Notebook component is used to display multiple pages within a tabbed interface, with tabs positioned either horizontally at the top or vertically on the left side. Notebook pages can be defined in two ways: by using slots or by passing a dedicated props configuration. Individual pages can be disabled using the isDisabled attribute, applied either directly on the slot node or in the page definition when pages are provided as props. When disabled, the corresponding tab is grayed out and becomes inactive.
Properties available for Notebook component are:
| Name | Type | Description |
| anchors | object | optional. Allow anchors navigation to elements inside tabs that are not visible. |
| className | string | optional. Classname set on the root of the component. |
| defaultPage | string | optional. Page id to display by default. |
| icons | array | optional. List of icons used in the tabs. |
| orientation | string | optional. Whether tabs direction is horizontal or vertical. |
| onPageUpdate | function | optional. Callback executed once the page has changed. |
| pages | array | optional. Contain the list of pages to populate from a template. |
Pager:
Location: @web/core/pager/pager
The Pager component is a lightweight pagination control used to navigate through data sets. Pages are defined by an offset and a limit, indicating the starting position and number of items per page. It displays the current range and total item count in a format such as “9–12 / 20”—for example, with an offset of 8, a limit of 4, and a total of 20 items. The component also provides Previous and Next buttons to easily move between pages.
<Pager offset="0" limit="80" total="50" onUpdate="doSomething" />
Properties available for pager are:
| Name | Type | Description |
| offset | number | Index of the first element of the page. It starts with 0 but the pager displays offset + 1. |
| limit | number | Size of the page. The sum of offset and limit corresponds to the index of the last element of the page. |
| total | number | Total number of elements the page can reach. |
| onUpdate | function | Function that is called when a page is modified by the pager. This function can be async, the pager cannot be edited while this function is executing. |
| isEditable | boolean | Allows you to click on the current page to edit it (true by default). |
| withAccessKey | boolean | Binds access key p on the previous page button and n on the next page one (true by default). |
SelectMenu:
Location: @web/core/select_menu/select_menu
This component is useful when you need more flexibility than the native select element provides. It allows you to define custom option templates, add search functionality, and organize options into grouped subsections for improved usability.
<SelectMenu
choices="choices"
>
<span class="select_menu_test">Select something</span>
<t t-set-slot="bottomArea" t-slot-scope="select">
<div t-if="select.data.searchValue">
<button class="btn text-primary" t-on-click="() => this.onCreate(select.data.searchValue)">
Create this article "<i t-esc="select.data.searchValue" />"
</button>
</div>
</t>
</SelectMenu>
Properties available are:
| Name | Type | Description |
| choices | array | optional. List of choices to display in the dropdown. |
| class | string | optional. Classname set on the root of the SelectMenu component. |
| groups | array | optional. List of group’s, containing choices to display in the dropdown. |
| multiSelect | boolean | optional. Enable multiple selections. When multiple selection is enabled, selected values are displayed as tags in the SelectMenu input. |
| togglerClass | string | optional. classname set on the toggler button. |
| required | boolean | optional. Whether the selected value can be unselected. |
| searchable | boolean | optional. Whether a search box is visible in the dropdown. |
| searchPlaceholder | string | optional. Text displayed as the search box placeholder. |
| value | any | optional. Current selected value. It can be from any kind of type. |
| onSelect | function | optional. Callback executed when an option is chosen. |
TagsList:
This component displays a list of tags styled as rounded pills. The tags can be static or editable, allowing items to be removed. You can control how many tags are shown using the itemsVisible prop; when the total exceeds this limit, a circular indicator appears next to the last visible tag showing the number of additional hidden items.
Location: @web/core/tags_list/tags_list
A tag has the following attributes:
- colorIndex: An optional identifier for the tag's color.
- icon: An optional icon that appears before the text on the tag.
- id: A unique identifier for the tag.
- img: An optional image displayed in a circular shape before the text.
- onClick: An optional callback function that allows the parent element to handle actions when the tag is clicked.
- onDelete: An optional callback function that enables the removal of the tag from the list, which must be managed by the parent element.
- text: The string of text displayed on the tag.
In summary, Owl components in Odoo 19 continue to provide a powerful and adaptable framework for building modern, interactive user interfaces. With enhanced support for customizable slots, props, and callback functions, developers can efficiently tailor components to meet specific functional and design needs. This flexibility enables the creation of highly responsive and user-focused interfaces while keeping development clean and structured. Thanks to Owl’s component-based architecture, applications in Odoo 19 are easier to maintain, scale, and seamlessly integrate across the platform, making Owl an essential tool for delivering rich user experiences and advanced functionality in Odoo 19.
To read more about An Overview of OWL Components in Odoo 18, refer to our blog An Overview of OWL Components in Odoo 18.