# OWL (Odoo glossary)

Canonical: https://erpfly.com/glossary/owl/
Last updated: September 14, 2026

**Definition:** OWL (Odoo Web Library) is the JavaScript UI framework Odoo builds and uses for its web client. OWL components are ES6 classes with a QWeb template and a setup() method, keep reactive state through hooks like useState, and are plugged into Odoo through registries such as fields, systray and main_components.

Also called: Odoo Web Library, Owl, Owl framework, Odoo OWL component

Open a sale order in Odoo 17, 18 or 19 and every piece of the screen, from the form renderer to each field, the dialogs and the systray icons, is an OWL component. Odoo develops the framework in its own repository, odoo/owl, and ships a copy inside the `web` module.

### What a component looks like

A component is a class that extends `Component`. It names its template in `static template`, declares expected props in `static props`, and does its initialisation in `setup()`. Odoo's docs are firm on that last point: don't write a constructor, because a JavaScript constructor can't be overridden the way Odoo code regularly overrides `setup()`. Hooks such as `useState`, `onWillStart` and Odoo's own `useService` are called inside `setup()`.

The template is QWeb syntax with a few extra directives like `t-on-click`, and it belongs in an XML file next to the JavaScript so it can be translated. Name it `your_addon.ComponentName` to avoid collisions.

Nothing reaches the browser until the files are listed in an asset bundle through the manifest, usually `web.assets_backend`.

### A small field widget

A float field shown as a coloured percentage, for the `margin_percent` field that `sale_margin` adds to sale orders:

```javascript
/** @odoo-module **/
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";

export class MarginBadgeField extends Component {
    static template = "sale_margin_badge.MarginBadgeField";
    static props = { ...standardFieldProps };

    get margin() {
        return this.props.record.data[this.props.name] || 0;
    }
}

export const marginBadgeField = {
    component: MarginBadgeField,
    supportedTypes: ["float"],
};

registry.category("fields").add("margin_badge", marginBadgeField);
```

```xml
<templates xml:space="preserve">
    <t t-name="sale_margin_badge.MarginBadgeField">
        <span t-attf-class="fw-bold {{ margin lt 0.18 ? 'text-danger' : 'text-success' }}"
              t-out="(margin * 100).toFixed(1) + '%'"/>
    </t>
</templates>
```

With both files under `static/src` and the folder added to `web.assets_backend`, an inherited form view uses it as `<field name="margin_percent" widget="margin_badge"/>`. The `lt` in the template is OWL's word form of `<`, which saves escaping it in XML.

### Version traps

**The `@odoo-module` comment.** Odoo 17 only transpiles a file as an ES module if it opens with that comment. From 18, every file under an addon's `static/src` is handled as a module automatically. Keeping the comment is harmless, so code meant for 17 to 19 should have it.

**Old registry format.** In 16 you registered the component class itself in the fields registry. Since 17 the entry is an object with a `component` key plus metadata like `supportedTypes` and `extractProps`. Plenty of tutorials still show the old way.

**Reading the wrong Owl docs.** At the time of writing, the main branch of odoo/owl is an Owl 3 alpha with a signal-based API, while the 17.0, 18.0 and 19.0 branches of Odoo all bundle the same Owl 2 release. Use the Owl 2 docs, where state comes from `useState`.

### When you don't need one

Most requests for a custom widget are covered by what's there: `widget="badge"`, `decoration-danger` on list columns, or a progress bar widget. We'd check those first. When a widget is justified, erpfly writes it as part of the addon, next to the inherited view that uses it, and delivers both in one pull request. The [Odoo customization](https://erpfly.com/odoo-customization/) page covers where widgets fit among other changes.

See also: https://erpfly.com/glossary/qweb/, https://erpfly.com/glossary/odoo-manifest/, https://erpfly.com/glossary/odoo-addon/, https://erpfly.com/glossary/xpath-view-inheritance/

### Sources

- [Owl components, Odoo 19 developer documentation](https://www.odoo.com/documentation/19.0/developer/reference/frontend/owl_components.html)
- [Registries, Odoo 19 developer documentation](https://www.odoo.com/documentation/19.0/developer/reference/frontend/registries.html)
- [Owl v2.8.4 README, odoo/owl on GitHub](https://github.com/odoo/owl/blob/v2.8.4/README.md)
- [odoo/tools/js_transpiler.py (18.0), odoo/odoo on GitHub](https://github.com/odoo/odoo/blob/18.0/odoo/tools/js_transpiler.py)