Ad – 728×90
🦉 Introduction

What is OWL JS? – The Odoo Web Library Explained

OWL (Odoo Web Library) is a modern, lightweight JavaScript UI framework built by Odoo that powers the entire Odoo web client since version 14. If you plan to develop for Odoo — customising modules, building views, creating backend widgets, or writing Odoo apps — OWL JS is the framework you must learn. In this lesson you will learn exactly what OWL is, how its component + template architecture works, how it compares to vanilla JavaScript, and you will write your very first working OWL component.

⏱️ 20 min read 🎯 Beginner 📅 Updated 2026

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.

ConceptOWL approach
ComponentsES6 classes extending Component
TemplatesXML strings with t-* directives (QWeb-style)
ReactivityuseState() — mutate directly, auto re-render
Eventst-on-click, t-on-input, etc.
Child componentsCapitalised XML tags: <MyButton/>
Size / deps~30 KB gzipped, zero dependencies
💡
Why should you care about OWL JS?

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:

JavaScript
// 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);
▶ Rendered HTML in browser
<div class="greeting">
  <h1>Hello from OWL JS!</h1>
  <p>I am a component. My name is HelloOWL.</p>
</div>

Three things to notice right away:

  1. Class-based — OWL components are ES6 classes that extend Component. If you have done the JavaScript OOP lessons, this syntax is familiar.
  2. XML template — The UI is defined as a static template using the xml`` tagged template literal. The template is standard XML enriched with t-* directives.
  3. mount() — The mount function 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.

JavaScript
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);
▶ Behaviour
Click "+ Increment" → Count: 1 → Count: 2 → ...
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" — the t-esc directive renders a value into the DOM, HTML-escaped for safety.
  • t-on-click="..." — the t-on-* directive attaches a DOM event listener. The part after t-on- is the event name (click, input, change, submit, etc.).
  • When state.count is mutated, OWL detects the change and re-renders only the affected parts of the DOM.
ℹ️
OWL reactivity vs React's setState

In React you call setState(newValue) to trigger an update. In OWL you mutate the object directlystate.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-heavyZero dependencies, modern JS/TS
Imperative DOM updatesDeclarative virtual DOM diffing
Mixin-based compositionComponent hierarchy + lifecycle hooks
No built-in reactivityProxy-based useState() reactivity
Difficult to unit testDesigned for easy unit testing
No TypeScript supportWritten 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.

XML (OWL Template)
<!-- 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'"/>
💡
All template values are JavaScript expressions

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.

Ad – 336×280

How OWL JS Renders the DOM

OWL uses a virtual DOM strategy. Here is the rendering pipeline step by step:

  1. 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).
  2. 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.
  3. DOM mounting — OWL converts the virtual DOM tree into actual DOM nodes and inserts them into the real browser DOM.
  4. 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.
ℹ️
Pre-compiled templates in production

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:

JavaScript (Vanilla — manual DOM)
// 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.
JavaScript (OWL JS — declarative)
// 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

FeatureDescription
Component modelES6 class-based components — clean OOP patterns
XML templatesStandard XML with t-* directives — readable and toolable
Reactive stateuseState() Proxy — mutate directly, auto re-renders
Lifecycle hooks11 hooks: onMounted, onPatched, onWillUnmount, and more
Props systemPass data to children with built-in type validation
SlotsFlexible content areas inside components — like Vue named slots
Environment / ServicesApp-wide state and services without prop drilling
Async renderingComponents can load data before first render (willStart)
Error handlingComponent-level error boundaries with onError
~30 KB gzippedTiny 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 VersionOdoo ReleaseStatus
OWL 1.xOdoo 14 – 15Legacy (still in many codebases)
OWL 2.xOdoo 16 – 17Stable / Current — what this course teaches
OWL 3.xOdoo 18+Alpha — signal-based reactivity, plugin system; API still changing
⚠️
OWL 3.x is alpha

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:

Shell
# Install via npm
npm install @odoo/owl
HTML (CDN)
<!-- 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-model are 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

Is OWL JS only for Odoo developers? +

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.

Do I need to know React or Vue before learning OWL JS? +

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.

What is the difference between OWL JS and QWeb? +

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.

Is OWL JS production-ready? +

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.

Can I learn OWL JS without learning Odoo? +

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.