How Widget Rendering Works in Odoo 19: Complete Guide
In Odoo 19, OWL stands for Odoo Web Library and acts as the frontend library for Odoo. All fields in a form view, cards in kanban view, and components on any other dashboard are rendered using OWL components. An understanding of OWL rendering makes it easier to develop custom widgets. This complete step by step technical guide walks through OWL rendering by building a fully functional Star Rating Widget, covering reactive state management, dynamic template rendering, event handling, and widget registration.
What You'll Learn:
- How to create a demo model with a priority field for widget testing
- How to build an OWL widget class extending Component
- How to use useState for reactive state management and auto re-rendering
- How to use t-foreach for rendering multiple elements dynamically
- How to register a custom widget in the Odoo fields registry
Understanding OWL in Odoo 19
OWL (Odoo Web Library) is the frontend component framework powering Odoo 19's user interface. Every field in a form view, card in a kanban view, and dashboard component is rendered using OWL components. The framework provides a Virtual DOM system with reactive state management, making UI development efficient and responsive.
OWL components are JavaScript objects extending the Component class. They use QWeb templates for rendering and provide directives such as t-foreach, t-att-class, and t-on-* for dynamic behaviour. When the component state changes, OWL automatically re-renders only the affected parts of the DOM, ensuring optimal performance.
Creating the Demo Model
We begin by creating a basic model called owl.task to experiment with the custom widget. This model includes a priority field that will implement our custom Star Rating widget.
# -*- coding: utf-8 -*-
from odoo import models, fields
class OwlTask(models.Model):
_name = 'owl.task'
_description = 'OWL Widget Rendering Demo Task'
name = fields.Char(required=True)
state = fields.Selection([
('new', 'New'),
('done', 'Done')
])
priority = fields.Integer(string='Priority')
The priority field is an integer field that will implement our custom OWL widget. Instead of a standard numeric input, users will interact with a visual star rating component.
Creating the OWL Widget
An OWL Widget is a JavaScript object which extends the Component class. It defines a template, props, state, and methods for handling user interaction.
/** @odoo-module **/
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
export class StarRatingWidget extends Component {
static template = "owl_widget_rendering_demo.StarRatingWidget";
static props = { ...standardFieldProps };
setup() {
this.state = useState({
hoverIndex: -1
});
this.stars = [1, 2, 3, 4, 5];
}
isFilled(index) {
const active = this.state.hoverIndex >= 0
? this.state.hoverIndex
: (this.props.value || 0);
return index <= active;
}
onStarClick(index) {
if (!this.props.readonly) {
this.props.record.update({
[this.props.name]: index
});
}
}
onStarHover(index) {
this.state.hoverIndex = index;
}
onStarLeave() {
this.state.hoverIndex = -1;
}
}
registry.category("fields").add("star_rating", {
component: StarRatingWidget,
displayName: "Star Rating",
supportedTypes: ["integer"],
});
Understanding the Rendering Logic
This Star Rating widget demonstrates four key aspects of OWL rendering: reactive state, dynamic list rendering, conditional CSS classes, and event handling. Each plays a critical role in making the widget interactive and efficient.
Reactive State with useState
When a variable is assigned to useState(), it becomes reactive. As soon as the value of hoverIndex changes via onStarHover(), OWL automatically re-renders the component. This is the foundation of OWL's reactivity model.
Dynamic Rendering with t-foreach
Stars are generated dynamically using t-foreach="stars" which loops through the array [1, 2, 3, 4, 5]. The t-key property identifies each star, enabling OWL to efficiently recycle DOM nodes during re-renders.
Conditional CSS with t-att-class
The t-att-class directive dynamically applies CSS classes. When isFilled(star) returns true, the filled class is added to highlight the star. The class binding is re-evaluated on every state change.
Event Handling with t-on-*
OWL templates use t-on-click, t-on-mouseenter, and t-on-mouseleave for DOM event binding. Events trigger component methods which update state, and OWL re-renders the affected parts of the UI automatically.
Reactive Rendering Using useState
this.state = useState({
hoverIndex: -1
});
When a variable is assigned to useState(), it becomes reactive. As soon as the value of hoverIndex is changed using this.state.hoverIndex = index, OWL detects the mutation and automatically re-renders the component. This is the foundation of OWL's reactivity system.
Rendering Multiple Items Using t-foreach
Within the template, stars are generated dynamically using t-foreach. The directive loops through the array [1, 2, 3, 4, 5] and creates five star elements. Using the t-key property helps identify each star uniquely, enabling OWL to efficiently recycle DOM nodes when re-rendering occurs. The t-att-class directive dynamically adds the CSS class based on the isFilled() method.
Event Handling in OWL
OWL templates support DOM event binding using t-on-* directives. When the user clicks a star, onStarClick() executes, which updates the field value through this.props.record.update(). OWL then re-renders the widget to reflect the new value. Similarly, t-on-mouseenter triggers onStarHover() to update the hover state dynamically, and t-on-mouseleave triggers onStarLeave() to reset it.
The OWL Template
★
The template uses owl="1" to indicate it is an OWL template. The outer div listens for the mouseleave event to reset hover state. The t-foreach directive generates five star spans, each with dynamic class binding and event handlers for click and hover interactions.
Registering the Widget
Finally, the widget must be registered inside the fields registry to make it available in Odoo's view system. Without registration, the widget cannot be used in XML views.
registry.category("fields").add("star_rating", {
component: StarRatingWidget,
displayName: "Star Rating",
supportedTypes: ["integer"],
});
Once registered, the widget can be used in any XML view with the widget attribute:
Rendering in Odoo 19 with OWL involves reactive state management and Virtual DOM rendering. By using directives such as t-foreach, t-att-class, and t-on-*, you can build highly interactive widgets by writing very little code. The Star Rating example demonstrates that OWL will render the user interface whenever there is any change in the state, which makes the widget very responsive and efficient.
Widget Registration Required
Registering the widget through registry.category("fields").add(...) links the OWL component with Odoo's view system. If this step is skipped, the widget cannot be used with the widget="star_rating" attribute in XML views and Odoo will fall back to the default field renderer for the data type.
Frequently Asked Questions
What makes the widget self-updating on hover or star clicks?
The widget uses useState() to manage state reactively. Every time there is an update such as changing hoverIndex, the mutation is detected by OWL and the concerned DOM elements get re-rendered without reloading the entire page.
What is the significance of using t-key within t-foreach tags?
Using t-key gives each rendered element a unique identifier. This enables OWL to compare old and new Virtual DOM nodes using their keys, allowing efficient DOM recycling and minimising unnecessary re-renders when the list changes.
Why is widget registration in the fields registry necessary?
Registering the widget through registry.category("fields").add(...) links the OWL component with Odoo's view system. Without registration, Odoo cannot map the widget="star_rating" attribute in XML views to the JavaScript component, and the field will render using the default widget for its data type.
Can I use the Star Rating widget in readonly mode?
Yes. The widget checks this.props.readonly inside onStarClick(). When the field is in readonly mode, clicks are ignored and the stars display the current value visually as filled or empty, making it suitable for both editing and display purposes.
What data types are supported by the Star Rating widget?
The widget is registered with supportedTypes: ["integer"], meaning it can only be used on integer fields. Attempting to use it on a float, char, or other field type will cause Odoo to fall back to the default widget for that data type.
Need Help with Odoo Development?
Our Odoo development experts can help you build custom OWL widgets, create interactive field components, optimise frontend rendering performance, and implement complex UI features for your Odoo modules.
About the author
Founder & Odoo Practice Lead, Braincuber Technologies
Founder of Braincuber. Has scoped and shipped 500+ Odoo implementations for US mid-market and global brands. Takes every founder call personally — no SDR layer between buyers and the people building the system.
