What is OWL JS? – The Simple Definition
OWL stands for Odoo Web Library. It is a component-based JavaScript UI framework created by Odoo — the world's most popular open-source ERP platform used by over 12 million users in 100+ countries. OWL is small (~30 KB gzipped), has zero external dependencies, and is written in TypeScript.
Like React and Vue, OWL lets you build user interfaces by composing reusable components — self-contained pieces of UI that manage their own data and render to the screen. Unlike those frameworks, OWL uses XML templates (inspired by Odoo's long-standing QWeb template engine) rather than JSX or .vue single-file components. If you know HTML, OWL's template syntax will feel immediately familiar.
| Concept | OWL approach |
|---|---|
| Components | ES6 classes extending Component |
| Templates | XML strings with t-* directives (QWeb-style) |
| Reactivity | useState() — mutate directly, auto re-render |
| Events | t-on-click, t-on-input, etc. |
| Child components | Capitalised XML tags: <MyButton/> |
| Size / deps | ~30 KB gzipped, zero dependencies |
Odoo 14 (2020) rewrote its entire front end using OWL. Every Odoo screen you see today — sales orders, invoices, CRM pipelines, inventory views, dashboards — is rendered by OWL. Learning OWL JS is the fastest path to a professional Odoo developer career.
Your First OWL Component
Here is the simplest possible OWL JS component. A component is an ES6 class that extends Component, and its UI is described by an XML template using the xml tagged template helper:
// OWL JS can be loaded from a CDN or installed via npm (@odoo/owl)
const { Component, mount, xml } = owl;
// 1. Define a component — an ES6 class
class HelloOWL extends Component {
// 2. Attach an XML template using the xml`` tag helper
static template = xml`
<div class="greeting">
<h1>Hello from OWL JS!</h1>
<p>I am a component. My name is HelloOWL.</p>
</div>
`;
}
// 3. Mount the root component to the real DOM
mount(HelloOWL, document.body);
<h1>Hello from OWL JS!</h1>
<p>I am a component. My name is HelloOWL.</p>
</div>
Three things to notice right away:
- Class-based — OWL components are ES6 classes that extend
Component. If you have done the JavaScript OOP lessons, this syntax is familiar. - XML template — The UI is defined as a static template using the
xml``tagged template literal. The template is standard XML enriched witht-*directives. - mount() — The
mountfunction takes a component class and a DOM element, renders the component tree, and attaches it to the real DOM.
OWL JS with Reactive State
Static HTML is only the beginning. OWL's real power is its reactivity system. Wrap any object with useState() and OWL will automatically re-render the component whenever that data changes — no manual DOM updates needed.
const { Component, mount, xml, useState } = owl;
class Counter extends Component {
// useState() wraps an object with a reactive Proxy.
// Any mutation triggers an automatic re-render.
state = useState({ count: 0 });
static template = xml`
<div class="counter">
<h2>Count: <t t-esc="state.count"/></h2>
<button t-on-click="() => state.count++">+ Increment</button>
<button t-on-click="() => state.count--">- Decrement</button>
<button t-on-click="() => state.count = 0">Reset</button>
</div>
`;
}
mount(Counter, document.body);
Click "Reset" → Count: 0
What just happened?
useState({ count: 0 })creates a reactive Proxy around the object. OWL tracks which component uses which reactive properties.t-esc="state.count"— thet-escdirective renders a value into the DOM, HTML-escaped for safety.t-on-click="..."— thet-on-*directive attaches a DOM event listener. The part aftert-on-is the event name (click,input,change,submit, etc.).- When
state.countis mutated, OWL detects the change and re-renders only the affected parts of the DOM.
In React you call setState(newValue) to trigger an update. In OWL you mutate the object directly — state.count++ works. OWL's proxy detects the mutation. This feels more natural and requires far less boilerplate, especially for nested objects.
OWL JS and Odoo – The Connection
Before Odoo 14, the Odoo web client was built with an older internal framework called Widget (based on Backbone.js). As modern web development evolved, the Odoo team designed OWL from the ground up to replace it. The goals were a cleaner component model, better performance, and first-class developer tooling.
| Old Odoo Widget (pre-14) | OWL JS (Odoo 14+) |
|---|---|
| Backbone.js + jQuery-heavy | Zero dependencies, modern JS/TS |
| Imperative DOM updates | Declarative virtual DOM diffing |
| Mixin-based composition | Component hierarchy + lifecycle hooks |
| No built-in reactivity | Proxy-based useState() reactivity |
| Difficult to unit test | Designed for easy unit testing |
| No TypeScript support | Written in TypeScript, full type safety |
OWL JS Template Directives — A Quick Tour
OWL templates are standard XML enriched with t-* directives for dynamic behaviour. Here is a quick tour of the most important directives you will use daily. Each has its own dedicated lesson later in this course.
<!-- t-esc: render a value (HTML-escaped, safe) -->
<p>Hello, <t t-esc="user.name"/>!</p>
<!-- t-if / t-else: conditional rendering -->
<span t-if="user.isAdmin">Admin</span>
<span t-else="">Guest</span>
<!-- t-foreach / t-as / t-key: loop over a list -->
<ul>
<li t-foreach="items" t-as="item" t-key="item.id">
<t t-esc="item.name"/>
</li>
</ul>
<!-- t-model: two-way binding on form inputs -->
<input t-model="state.query" placeholder="Search..."/>
<p>You typed: <t t-esc="state.query"/></p>
<!-- t-on-*: attach event listeners -->
<button t-on-click="handleSave">Save</button>
<input t-on-input="onInput" t-on-keydown="onKeyDown"/>
<!-- t-att-class: dynamic CSS classes -->
<div t-att-class="{ active: isActive, error: hasError }">...</div>
<!-- Capitalised tag = child component -->
<UserCard name="'Alice'" role="'Admin'"/>
The values of t-* attributes are evaluated as JavaScript expressions in the context of the component. t-esc="user.name" means "evaluate the JS expression user.name and render the result". You can use any valid JS expression: ternaries, method calls, arithmetic, etc.
How OWL JS Renders the DOM
OWL uses a virtual DOM strategy. Here is the rendering pipeline step by step:
- Template compilation — When the app first loads, OWL compiles your XML templates into optimised JavaScript rendering functions. This happens once at startup (or is pre-compiled on the server in production Odoo).
- Initial render — OWL executes the rendering function with your initial state/props to produce a virtual DOM tree — a plain JavaScript object tree describing the HTML.
- DOM mounting — OWL converts the virtual DOM tree into actual DOM nodes and inserts them into the real browser DOM.
- Reactive updates — When state or props change, OWL runs the rendering function again, diffs the new virtual DOM against the previous one, and applies only the minimal DOM changes needed. Unchanged nodes are never touched.
In production Odoo, all OWL templates are pre-compiled on the server and sent to the browser as ready-to-run JavaScript functions. This eliminates the client-side compilation step entirely, making the initial page load significantly faster.
OWL JS vs Vanilla JavaScript
To appreciate what OWL gives you, compare the same interactive counter built with vanilla JavaScript and with OWL JS:
// Vanilla JS — you manage the DOM manually
let count = 0;
const display = document.getElementById('count');
document.getElementById('increment').addEventListener('click', () => {
count++;
display.textContent = count; // Must remember to update DOM every time
});
// For a small counter this is fine.
// For a complex Odoo view with hundreds of fields, events, and conditions,
// manual DOM management becomes unmaintainable very quickly.
// OWL — declare your state and your template; OWL keeps the DOM in sync
class Counter extends Component {
state = useState({ count: 0 });
static template = xml`
<div>
<p>Count: <t t-esc="state.count"/></p>
<button t-on-click="() => state.count++">+</button>
</div>
`;
// Zero manual DOM updates — OWL handles everything
}
// As your UI grows, OWL scales cleanly:
// - Split into sub-components
// - Pass data via props
// - Share services via the environment
Key Features of OWL JS at a Glance
| Feature | Description |
|---|---|
| Component model | ES6 class-based components — clean OOP patterns |
| XML templates | Standard XML with t-* directives — readable and toolable |
| Reactive state | useState() Proxy — mutate directly, auto re-renders |
| Lifecycle hooks | 11 hooks: onMounted, onPatched, onWillUnmount, and more |
| Props system | Pass data to children with built-in type validation |
| Slots | Flexible content areas inside components — like Vue named slots |
| Environment / Services | App-wide state and services without prop drilling |
| Async rendering | Components can load data before first render (willStart) |
| Error handling | Component-level error boundaries with onError |
| ~30 KB gzipped | Tiny footprint — no dependencies — fast to load |
OWL JS Versions
There are three major versions of OWL. This course focuses on OWL 2.x, the stable version used in Odoo 16 and 17 — the most widely deployed Odoo versions today.
| OWL Version | Odoo Release | Status |
|---|---|---|
| OWL 1.x | Odoo 14 – 15 | Legacy (still in many codebases) |
| OWL 2.x | Odoo 16 – 17 | Stable / Current — what this course teaches |
| OWL 3.x | Odoo 18+ | Alpha — signal-based reactivity, plugin system; API still changing |
OWL 3.0 introduces a new signal-based reactivity system and a plugin API. Its public API is still subject to change. This course teaches OWL 2.x. The core concepts — components, XML templates, lifecycle hooks, props — are very similar across versions, so what you learn here transfers directly.
Using OWL JS Standalone (Without Odoo)
OWL is not locked to the Odoo platform. You can use it as a standalone UI framework for any web project. Install it via npm or load it from a CDN:
# Install via npm
npm install @odoo/owl
<!-- Load OWL from the GitHub release (IIFE build) -->
<script src="https://cdn.jsdelivr.net/npm/@odoo/owl/dist/owl.iife.js"></script>
<!-- owl is now available as a global variable -->
<script>
const { Component, mount, xml, useState } = owl;
// ... your app code
</script>
📋 Summary
- OWL stands for Odoo Web Library — the JavaScript UI framework built by Odoo, powering its web client since version 14.
- OWL components are ES6 classes with XML templates using
t-*directives. useState()creates a reactive Proxy — mutating state directly triggers automatic re-renders, no manual DOM updates needed.t-esc,t-if,t-foreach,t-on-*,t-modelare the most-used template directives.- OWL uses a virtual DOM for efficient updates — unchanged DOM nodes are never touched.
- OWL is ~30 KB gzipped with zero dependencies — fast and lean.
- This course teaches OWL 2.x, used in Odoo 16 and 17 — the most widely deployed stable versions.
Frequently Asked Questions
OWL JS can be used as a standalone framework for any web project — it is available on npm as @odoo/owl and works independently of Odoo. However, in practice the overwhelming majority of developers who learn OWL JS do so to develop for the Odoo platform. Outside of Odoo, React, Vue, and Svelte have much larger communities and ecosystems. If you want to build Odoo modules or customise Odoo, OWL is the only choice for front-end work.
No — OWL is a standalone framework with its own concepts. Prior React or Vue experience is not required. You do need solid JavaScript knowledge, particularly ES6 classes, modules, arrow functions, and destructuring. See our Prerequisites page for a complete checklist with links to all the JavaScript lessons you should complete first.
QWeb is Odoo's older server-side XML template engine used for PDF reports and legacy Odoo views. OWL's template syntax is inspired by QWeb — they share t-if, t-foreach, and t-esc — but OWL is a full client-side JavaScript framework, not just a server template engine. Think of QWeb as OWL's ancestor; OWL is the modern, reactive, client-side evolution.
Yes. OWL 2.x is the production framework for Odoo 16 and 17, serving millions of users across thousands of enterprise deployments worldwide. It is battle-tested and actively maintained by the Odoo core team. OWL 3.x is still in alpha and not yet recommended for production use.
Yes. This course teaches OWL JS as a standalone framework. You will learn all the core OWL concepts — components, templates, state, lifecycle, slots, services — without needing a running Odoo instance. Later lessons cover Odoo-specific integration (calling Odoo RPC methods, using Odoo services), which require an Odoo environment, but you can complete most of this course without one.