Skip to Content
  • Home
  • Book
  • Blog
  • About us
  • Help
  • Contact us
  •  [email protected]
  • Sign in
  • English (US) Español (BO)
  • Simplify It S.R.L.
    • Contact Us
Simplify It S.R.L.
      • Home
      • Book
      • Blog
      • About us
      • Help
      • Contact us
    •  [email protected]
    • English (US) Español (BO)
    • Sign in
    • Contact Us

    Chapter 5: Hello, OWL! Your First Component

  • All Blogs
  • OWL Book
  • Chapter 5: Hello, OWL! Your First Component
  • August 25, 2026 by
    Chapter 5: Hello, OWL! Your First Component
    Grover Menacho

    Why this chapter? Every OWL feature you'll learn later builds on the same three-file skeleton you set up here — get this part solid and the rest of the book is additions, not surprises. What is Odoo trying to solve with this? A predictable, consistent structure for every custom UI piece in the ecosystem, so any Odoo developer can open any addon's static/src folder and immediately know where to look. Real-world application: This is the exact scaffolding you'll reuse on day one of any client project — a custom dashboard widget, a portal form, a kanban card override — before a single line of business logic gets written.

    Congratulations on making it through the fundamentals! You now have all the JavaScript knowledge you need to start building with OWL. In this chapter, the theory ends and the practical application begins. We are going to build our very first, classic "Hello, World!" component.

    This is the moment where everything you've learned—classes, modules, template literals, destructuring, and the declarative mindset—comes together. By the end of this chapter, you'll have a working OWL component running inside Odoo. Let's get started.


    Anatomy of an OWL Component

    An OWL component is not a single file. It's a small, self-contained ecosystem typically made of three files, each with a specific responsibility. This separation of concerns is a core principle that keeps your code clean, organized, and maintainable.

    Think of it like building a house:

    1. The JavaScript File (.js): This is the brain of your component. It holds the logic, the state, the methods, and defines how the component behaves. This is where all your modern JavaScript skills come into play.

    2. The XML File (.xml): This is the skeleton. It defines the structure and layout of your component's UI using QWeb, Odoo's powerful template language. This determines what users see.

    3. The CSS/SCSS File (.scss): This is the skin. It contains all the styling rules to make your component look professional and match Odoo's design system. (We'll cover styling in detail in later chapters.)

    For our first component, we'll focus on the first two: the JavaScript and the XML file. Don't worry about styling for now—let's get the functionality working first.


    Setting Up Your Module Structure

    Before we write any code, let's establish a proper file structure. Organization matters, especially as your components grow in complexity.

    Create the following directory structure in your Odoo module:

    my_odoo_module/
    |-- __manifest__.py
    |-- static/
    |   |-- src/
    |       |-- components/
    |           |-- hello_world/
    |               |-- hello_world.js
    |               |-- hello_world.xml
    |               |-- hello_world.scss (we'll add this later)
    |-- views/
        |-- hello_world_views.xml
    

    Why this structure? - static/src/components/: This is the standard location for OWL components in Odoo modules - Component-specific folders: Each component gets its own folder (hello_world/) to keep related files together - Consistent naming: Files are named after the component for easy identification


    The JavaScript File: The Component's Logic

    Let's create our first component. Create the file my_odoo_module/static/src/components/hello_world/hello_world.js:

    /** @odoo-module **/
    
    import { Component } from "@odoo/owl";
    import { registry } from "@web/core/registry";
    
    export class HelloWorld extends Component {
        static template = "my_odoo_module.HelloWorld";
    
        setup() {
            console.log("HelloWorld component is being set up!");
        }
    }
    
    // Register the component as a client action so Odoo can display it
    registry.category("actions").add("hello_world_app", HelloWorld);
    

    Let's break this down line by line:

    /** @odoo-module **/: This is a special comment that tells the Odoo asset system to treat this file as a module. It's mandatory for all JavaScript files in Odoo modules. Without it, your component won't be recognized.

    import { Component } from "@odoo/owl";: Here we're using the destructuring import syntax we learned about. We're extracting the Component class from the OWL library. This gives us access to all the powerful features of OWL components.

    export class HelloWorld extends Component { ... }: This line does several important things: - export: Makes our class available to other parts of the system (remember modules?) - class HelloWorld: Defines our component class with a descriptive name - extends Component: Inherits all the OWL functionality (remember class inheritance?)

    static template = "my_odoo_module.HelloWorld";: This is the crucial link between our JavaScript logic and our XML template. The name must be: - Unique across your entire Odoo instance - Follow the convention: module_name.ComponentName - Match exactly what we define in the XML file

    setup() { ... }: This is OWL 2.0's modern approach to component initialization. It replaces the old constructor pattern and runs when the component is created. The console.log will help us see when our component is working.

    registry.category("actions").add("hello_world_app", HelloWorld);: This registers our component in Odoo's actions registry under the tag hello_world_app. In a moment, we'll create a client action on the server side whose tag field matches this name—that's how Odoo knows which component to render when the action is triggered.

    Understanding the Component Lifecycle

    Even in this simple example, OWL is doing a lot behind the scenes:

    1. Component Creation: OWL creates an instance of our HelloWorld class
    2. Setup Execution: Our setup() method runs, allowing us to initialize state and services
    3. Template Rendering: OWL finds the template we specified and renders it
    4. DOM Insertion: The rendered HTML is inserted into the page

    The XML File: The Component's Template

    Now let's create the template that our JavaScript file references. Create the file my_odoo_module/static/src/components/hello_world/hello_world.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <templates xml:space="preserve">
    
        <t t-name="my_odoo_module.HelloWorld" owl="1">
            <div class="hello-world-component">
                <div class="alert alert-info">
                    <h1 class="alert-heading">
                        <i class="fa fa-rocket me-2"></i>
                        Hello, OWL World!
                    </h1>
                    <p class="mb-0">
                        This is my first OWL component, and it's working perfectly!
                    </p>
                    <hr class="my-3"/>
                    <p class="mb-0">
                        <strong>Component Name:</strong> HelloWorld<br/>
                        <strong>Template:</strong> my_odoo_module.HelloWorld<br/>
                        <strong>Framework:</strong> OWL 2.0
                    </p>
                </div>
            </div>
        </t>
    
    </templates>
    

    Let's examine the important parts:

    <templates xml:space="preserve">: This is the standard wrapper for all Odoo template files. The xml:space="preserve" ensures that whitespace in your templates is handled correctly.

    <t t-name="my_odoo_module.HelloWorld" owl="1">: This is where the magic happens: - <t>: A QWeb template tag that doesn't render itself, just its contents - t-name="my_odoo_module.HelloWorld": The unique identifier that must exactly match the static template property in our JavaScript class - owl="1": This crucial attribute tells Odoo's QWeb engine to process this template using the OWL renderer instead of the legacy renderer

    The HTML Content: Inside the <t> tag is standard HTML that will become the actual DOM structure of your component. I've used Bootstrap classes (which Odoo includes) to make it look professional right away.

    Why This Template Structure?

    • Semantic HTML: We use proper HTML5 elements and Bootstrap classes
    • Clear visual feedback: The alert styling makes it obvious when the component renders
    • Debugging information: We include the component and template names for easy identification
    • Professional appearance: Even a "Hello World" component should look good in Odoo

    Registering the Component with Odoo

    We've created the component files, but Odoo doesn't know about them yet. We need to register them with the asset system.

    Step 1: Update the Module Manifest

    Open your module's __manifest__.py file and add the new files to the assets dictionary:

    # In __manifest__.py
    {
        'name': 'My OWL Hello World Module',
        'version': '1.0',
        'depends': ['base', 'web'],
        'data': [
            'views/hello_world_views.xml',
        ],
        'assets': {
            'web.assets_backend': [
                'my_odoo_module/static/src/components/hello_world/hello_world.js',
                'my_odoo_module/static/src/components/hello_world/hello_world.xml',
            ],
        },
        'installable': True,
        'auto_install': False,
    }
    

    Important points about assets: - web.assets_backend: This bundle loads on the Odoo backend (where most business applications live) - Order matters: JavaScript files should generally come before XML files - Path accuracy: Double-check your file paths—typos here will cause silent failures

    Step 2: Create a View to Display the Component

    Create a view file at my_odoo_module/views/hello_world_views.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <odoo>
        <data>
    
            <!-- Define a client action to display our component.
                 The "tag" must match the name used in registry.category("actions").add(...) -->
            <record id="action_hello_world" model="ir.actions.client">
                <field name="name">Hello OWL World</field>
                <field name="tag">hello_world_app</field>
                <field name="target">current</field>
            </record>
    
            <!-- Add a menu item so users can find your component -->
            <menuitem id="menu_hello_world"
                      name="Hello OWL World"
                      action="action_hello_world"
                      parent="base.menu_administration"
                      sequence="100"/>
    
        </data>
    </odoo>
    

    Understanding this structure:

    Client Action: Unlike regular Odoo views (form, list, etc.), OWL components often use "client actions"—special actions that render custom JavaScript applications instead of standard views.

    The tag field is the link: When the user triggers this action, Odoo looks up hello_world_app in the JavaScript actions registry and finds the HelloWorld component we registered there. No server-side template is needed—the component's own QWeb template does the rendering.

    Menu Integration: The <menuitem> gives users a way to access your component through Odoo's standard menu system.


    Testing Your Component

    Now for the moment of truth! Let's get your component running:

    Step 1: Install/Upgrade Your Module

    # If this is a new module
    ./odoo-bin -d your_database -i my_odoo_module --dev=all
    
    # If you're updating an existing module
    ./odoo-bin -d your_database -u my_odoo_module --dev=all
    

    The --dev=all flag is crucial—it ensures that your JavaScript and XML changes are loaded immediately without needing to restart the server.

    Step 2: Navigate to Your Component

    1. Log into your Odoo instance
    2. Go to Settings (or wherever you placed your menu item)
    3. Click on "Hello OWL World"

    If everything worked correctly, you should see your beautiful component rendered with the Bootstrap alert styling!

    Step 3: Verify in Browser DevTools

    Open your browser's DevTools (F12) and:

    1. Check the Console: You should see the message "HelloWorld component is being set up!"
    2. Inspect the Elements: You should see the HTML structure from your template
    3. Check the Network tab: Verify that your .js and .xml files were loaded

    Common Issues and Solutions

    "Component not found" error: - Check that your static template property exactly matches the t-name in your XML - Verify that both files are listed in __manifest__.py - Make sure you've upgraded your module

    "Template not found" error: - Confirm the owl="1" attribute is present in your template - Check for typos in the template name - Verify the XML file is valid (no syntax errors)

    Component doesn't appear: - Check the browser console for JavaScript errors - Verify your client action and menu item are correctly defined - Make sure you're running with --dev=all

    Styling looks wrong: - Odoo includes Bootstrap by default, so our classes should work - Try inspecting the elements to see what CSS is being applied - Remember that we haven't added custom CSS yet—that comes in later chapters


    What You've Accomplished

    Congratulations! You've just built your first OWL component and integrated it into Odoo. This seemingly simple example demonstrates several crucial concepts:

    Component Architecture: You've seen how JavaScript logic and XML templates work together

    Modern JavaScript in Practice: You've used import, export, class, and extends in a real application

    Odoo Integration: You understand how OWL components fit into Odoo's module system

    Development Workflow: You know how to create, register, and test components

    Debugging Foundation: You have the basics for troubleshooting when things go wrong

    TL;DR: An OWL component is a JS class (static template + setup()) paired with a QWeb XML template (t-name, owl="1") whose names must match exactly; register it with registry.category("actions").add(tag, Component) and an ir.actions.client record sharing that tag.

    Try it yourself: This chapter's example is available as an installable Odoo 19 addon: simplifyit_owl_book_ch5_ex1. Installation instructions are in the repository README.

    Exercises

    1. Personalize the greeting. Change the component so it greets a specific name (e.g., "Hello, Ana!") by adding a plain JavaScript property in setup() and displaying it with t-esc in the template.
    2. Break it on purpose. Rename the t-name in your XML file so it no longer matches static template in the JavaScript file, reload the page, and read the exact error Odoo shows you in the console. Then fix it. Recognizing this error message will save you time later.
    3. Add a second action. Add a button that calls a new method logging a message to the console with console.log, and verify it in the browser DevTools Console tab from Chapter 2.

    What's Next?

    Right now, this component is static — it shows the same text no matter what. Chapter 6 makes it dynamic: you'll learn the QWeb directives (t-esc, t-if, t-foreach, and more) that let your template react to data coming from the JavaScript side.

    in OWL Book
    Chapter 6: Rendering with QWeb Templates
    Reading the whole book? Take it with you — free, no sign-up.
    PDF EPUB All chapters
    Useful Links
    • Home
    • About us
    • Blog
    • The OWL 2.0 book
    • Help
    • Contact us
    About us

    Simplify It S.R.L. is an Odoo implementation and development firm based in La Paz, Bolivia, working with companies across Latin America and North America. We have been building on Odoo since version 6.1: custom modules, version migrations and Bolivian localization.

    We are also the authors of the OWL 2.0 book, published free chapter by chapter on our blog.

    Connect with us
    • Contact us
    • [email protected]
    • +591 65144144
    • La Paz, Bolivia
    Follow us
    Copyright © Simplify It S.R.L.
    English (US) | Español (BO)
    Powered by Odoo - Create a free website