In this post, I want to focus on Odoo’s OWL framework — the first major layer of frontend complexity in Odoo’s web stack — and question whether building it was truly necessary, or whether it was an avoidable source of long-term complexity justified by the familiar argument: “it’s an ERP, so it must be complex.”
For context, OWL (Odoo Web Library) is the JavaScript framework used to power Odoo’s frontend components, including the dashboard, backend UI, and parts of the website.
According to Odoo, OWL was built from scratch to solve a specific problem: allowing third-party developers to override and extend frontend components defined by other modules without modifying core files or losing changes during upgrades.
On paper, this goal is reasonable — even admirable. However, the conclusion that this required building an entirely new frontend framework is far more questionable.
The same goal is already achievable in all major modern frontend frameworks (React, Vue, Angular) through well-established mechanisms such as component composition, slots, higher-order components, dependency injection, extension APIs, and schema-driven rendering.
What a Mature Frontend Framework Would Have Provided To Odoo ?
By using a mature frontend framework would have provided several major advantages:
- Clear, evolving documentation Mature frameworks have continuously updated documentation that closely tracks real-world usage and features. In contrast, Odoo’s documentation is often incomplete, outdated, or misleading — a problem significant enough to warrant a dedicated post.
- Security responsiveness Modern frontend ecosystems respond rapidly to security disclosures, issuing patches without requiring a full application upgrade. In Odoo, frontend fixes are tightly coupled to backend releases, making security patching significantly more disruptive.
- A vast ecosystem From form builders and schema validators to accessibility tooling, testing frameworks, and UI component libraries — modern ecosystems provide solutions that Odoo either reimplements partially or lacks entirely.
- Developer familiarity Frontend developers today are already fluent in React, Vue, or Angular. OWL introduces a proprietary mental model that developers must learn on top of Odoo’s already complex backend abstractions.
Instead, Odoo now maintains a hybrid frontend stack where OWL coexists with legacy code — including multiple versions of jQuery (2.x and 3.x) still present in parts of the system. This alone should raise questions about long-term maintainability.
Before comparing OWL directly to React or Vue, it’s important to understand how OWL actually works.
At its core, OWL consumes XML definitions sent from the backend and dynamically builds a component tree on the frontend. These XML views are parsed, interpreted, and translated into JavaScript-rendered UI components.
For example, consider a simple OWL form definition:
<form>
<field name="my_model_field_name"/>
</form>
The frontend receives this XML and uses the OWL runtime to translate it into rendered UI components.
- Parses the XML into a component tree
- Issues additional requests to fetch model field metadata
- Decides which component to render based on field definitions
- Optionally uses the
widgetattribute to select a custom renderer
In other words, OWL acts as a runtime XML interpreter that generates UI behavior dynamically.
The Hard Truth About OWL
OWL’s flexibility does not come from a fundamentally new idea — it comes from deferring structure and behavior decisions to runtime. This is not unique, nor does it require a custom framework.
The same level of flexibility can be achieved in React for example by using:
- Schema-driven rendering
- Plugin registries
- Declarative extension points
- Controlled overrides via dependency injection
- Permissioned component replacement
Many systems already parse XML, JSON, or DSLs and render them safely within mature frameworks — without reinventing rendering lifecycles, state management, reactivity, or tooling.
By choosing to build OWL, Odoo accepted the burden of:
- Maintaining a proprietary framework
- Rebuilding tooling that already exists elsewhere
- Fragmenting frontend knowledge
- Coupling frontend evolution to backend architectural constraints
In a follow-up section, I’ll demonstrate how Odoo’s XML-based UI model could be rendered and overridden cleanly using React.js, achieving the same extensibility without introducing a custom framework. The goal isn’t to claim OWL is unusable — but to show that building it was an unnecessary architectural choice that added long-term cost without solving a novel problem.
- A Simple OWL Component vs a React Component To make the discussion concrete, let’s compare a minimal OWL component with an equivalent React component. OWL Component (Simplified)
/** @odoo-module **/
import { Component, xml } from "@odoo/owl";
export class Hello extends Component {
static template = xml`
<div>
<h1>Hello <t t-esc="props.name"/></h1>
</div>
`;
}
Usage is typically tied to XML view definitions sent from the backend, and the component lifecycle, state handling, and rendering behavior are governed by OWL’s custom runtime.
React Component (Equivalent)
function Hello({ name }) {
return (
<div>
<h1>Hello {name}</h1>
</div>
);
}
At a surface level, both components are trivial. The key difference isn’t syntax — it’s ecosystem gravity.
In React:
- Component composition is standard
- Tooling (linting, testing, profiling) is mature
- State, effects, and error boundaries are well-defined
- Integration with schema-driven rendering is commonplace
OWL must reimplement or approximate much of this — while also introducing a proprietary mental model that developers must learn in addition to Odoo’s backend abstractions.
- React Pseudo-Implementation That Mirrors OWL Overrides A common defense of OWL is that it allows runtime UI overrides — the ability for modules to replace or extend UI behavior dynamically without editing core code.
This is not unique to OWL.
Below is a simplified React-based architecture that mirrors the same capability.
Component Registry (Core)
const ComponentRegistry = new Map();
export function registerComponent(name, component) {
ComponentRegistry.set(name, component);
}
export function getComponent(name) {
return ComponentRegistry.get(name);
}
Schema-Driven Renderer
function Renderer({ schema }) {
return schema.map((node) => {
const Component = getComponent(node.type);
return <Component key={node.id} {...node.props} />;
});
}
Default Registration (Core Module)
registerComponent("field:text", TextField);
registerComponent("field:number", NumberField);
Override by Addon / Third-Party Module
registerComponent("field:text", CustomTextField);
No core files edited. No fork. No rebuild of a framework just to justify ERP is complex and it needs to have complex UI framework with no ecosystem no tooling no documentation.
by using this simple structure at least someone can achieve almost everything in OWL.js while also if needed they can leverage existing ecosystem nice tooling.
As well as:
- Runtime replacement
- Controlled extension points
- Clear override ownership
- Predictable behavior
The same pattern scales to:
- Permissions
- Feature flags
- User-specific overrides
- Context-based rendering
- A better implementation that handles menus and UI translations.
This is effectively what OWL does — but OWL bundles it with a custom rendering engine, lifecycle model, and tooling stack.
3. “But React Can’t Do Runtime UI Overrides”
This is the most common objection — and it’s based on a misconception.
React absolutely supports runtime UI overrides.
What it does not support is implicit, unstructured mutation — and that’s a feature, not a limitation.
Runtime overrides in React are achieved via:
- Registries
- Context providers
- Dependency injection patterns
- Plugin systems
- Schema-driven rendering
All of which are:
- Explicit
- Traceable
- Testable
- Tooling-friendly
OWL’s approach relies heavily on runtime interpretation of XML combined with implicit behavior resolution. This provides flexibility — but at the cost of:
- Debuggability
- Static analysis
- Predictable failure modes
React’s ecosystem favors controlled extensibility over unrestricted mutation. That makes large systems more maintainable over time, especially when multiple teams and third-party developers are involved.
In other words:
- OWL optimizes for maximum runtime freedom
- React optimizes for sustainable extensibility
Closing Clarification
The argument here is not that OWL is unusable, nor that Odoo developers are unaware of existing frameworks.
The argument is that the problem OWL solves was already solved, and rebuilding a framework to solve it again introduced long-term cost without introducing a fundamentally new capability.
Odoo didn’t just choose flexibility — it chose to own the entire frontend stack. And owning the stack means owning every limitation, bug, security issue, and ecosystem gap that comes with it.
That is the real cost of reinventing the web stack. This does not make Odoo unusable — but it does make its long-term evolution far more expensive than it needed to be.
Top comments (0)