In modern web applications, user experience plays a decisive role in engagement and conversions. Instead of navigating users away from the current page, modern interfaces prefer contextual UI elements that feel lightweight, fast, and intuitive. As part of Odoo ERP, version 19 introduces a powerful Bottom Sheet Service combined with the new Interaction class, enabling developers to build modern, mobile-first UI patterns directly into Odoo websites.

Bottom sheets are UI components that slide up from the bottom of the screen to display additional information or actions without interrupting the current context. They are commonly used in mobile applications, but Odoo 19 brings this concept seamlessly to web experiences, especially for eCommerce and website-driven workflows.

This guide walks through how to use the Bottom Sheet Service in Odoo 19, explains the underlying architecture, and demonstrates how to implement a complete working example using OWL components and the Interaction system.


What Is a Bottom Sheet?

A bottom sheet is a modal-like UI element that appears from the bottom of the viewport. Unlike traditional popups or full-page navigations, bottom sheets:

  • Preserve the current page context

  • Provide quick access to details or actions

  • Feel natural on mobile and tablet devices

  • Support smooth animations and gestures

In Odoo 19, bottom sheets are managed by a dedicated service that handles rendering, animation, dismissal logic, and interaction events automatically.


Bottom Sheet Architecture in Odoo 19

Implementing a bottom sheet in Odoo 19 involves multiple layers working together:

  1. OWL Component – Defines the content shown inside the bottom sheet

  2. Interaction Class – Handles DOM interactions and user events (replaces publicWidget)

  3. Bottom Sheet Service – Manages lifecycle, animations, and dismissal

  4. Controller – Serves the web route and provides data

  5. QWeb Template – Renders the webpage and trigger elements

This modular architecture ensures clean separation of concerns and maintainable frontend code.

Step 1: Creating the OWL Component

The OWL component represents the UI rendered inside the bottom sheet. In this example, we create a simple product card that displays image, name, price, and description.

JavaScript Component

/** @odoo-module **/ import { Component } from "@odoo/owl"; export class ProductCard extends Component { static template = "bottom_sheet_test.ProductCard"; static props = { product: Object, }; }

XML Template

<?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> <t t-name="bottom_sheet_test.ProductCard"> <div class="product-card-sheet"> <div class="product-image"> <img t-att-src="props.product?.image or '/web/static/img/placeholder.png'" class="img-fluid"/> </div> <div class="product-info"> <h3 t-esc="props.product?.name"/> <div class="product-price"> $<t t-esc="props.product?.price"/> </div> <p t-esc="props.product?.description"/> </div> </div> </t> </templates>

The OWL component receives product data dynamically and renders it inside the bottom sheet. This makes it reusable and scalable across multiple pages.

Step 2: Using the Interaction Class

In Odoo 19, publicWidget has been replaced by the Interaction class. This new system provides a cleaner API and better lifecycle handling.

/** @odoo-module **/ import { registry } from "@web/core/registry"; import { Interaction } from "@web/public/interaction"; import { ProductCard } from "../components/product_card/product_card"; export class BottomSheetInteraction extends Interaction { static selector = ".bottom-sheet-demo-page"; dynamicContent = { ".trigger-product-sheet": { "t-on-click": this.onTriggerProductSheet }, }; setup() { super.setup(); this.bottomSheetService = this.services.bottom_sheet; } onTriggerProductSheet(ev) { const productData = { name: ev.target.dataset.productName, price: ev.target.dataset.productPrice, description: ev.target.dataset.productDescription, image: ev.target.dataset.productImage, }; this.bottomSheetService.add( ev.target, ProductCard, { product: productData }, { class: "product-bottom-sheet" } ); } } registry.category("public.interactions").add( "BottomSheetInteraction", BottomSheetInteraction );

Key Improvements Over publicWidget

  • Uses Interaction instead of publicWidget.Widget

  • Declarative event binding with dynamicContent

  • Access services through this.services

  • Cleaner, future-proof API

Step 3: Creating the Controller

The controller provides backend data and renders the webpage.

from odoo import http from odoo.http import request class BottomSheetController(http.Controller): @http.route('/bottom-sheet-demo', type='http', auth='public', website=True) def bottom_sheet_demo(self): products = request.env['product.product'].search_read( [], ['name', 'list_price', 'description'], limit=3 ) return request.render('your_module.bottom_sheet_demo_page', { 'products': products, })

This approach keeps frontend logic lightweight while using Odoo’s ORM to fetch data.

Step 4: Creating the Website Template

The QWeb template renders product cards and trigger buttons.

<button class="btn btn-primary trigger-product-sheet" t-att-data-product-name="product['name']" t-att-data-product-price="product['list_price']" t-att-data-product-description="product['description']"> View Details </button>

Using HTML data attributes allows seamless data transfer from the DOM to the Interaction logic without additional AJAX calls.

Step 5: Asset Registration

Register assets in the module manifest:

'assets': { 'web.assets_frontend': [ 'your_module/static/src/components/product_card/product_card.xml', 'your_module/static/src/components/product_card/product_card.js', 'your_module/static/src/interactions/bottom_sheet_interaction.js', ], },

This ensures the OWL component and Interaction class load correctly on the website.

User Experience Benefits

Bottom sheets significantly improve usability by:

  • Avoiding page reloads

  • Keeping users focused

  • Providing mobile-first interactions

  • Supporting multiple dismissal methods (scroll, ESC, back button)

They are especially valuable in eCommerce, product catalogs, and informational popups.

Best Practices

  • Keep bottom sheet content concise

  • Use reusable OWL components

  • Avoid heavy logic inside interactions

  • Leverage services instead of custom DOM hacks

  • Design for mobile-first layouts

Conclusion

The Bottom Sheet Service in Odoo 19 represents a major step forward in frontend development for Odoo websites. By combining OWL components with the new Interaction class, developers can build modern, responsive, and non-intrusive UI elements that align with today’s UX standards.

Moving away from legacy publicWidget patterns, this approach provides a clean, scalable, and maintainable structure that fits perfectly into the evolving Odoo frontend ecosystem. Whether you’re building eCommerce experiences or interactive website features, bottom sheets are a powerful addition to your Odoo 19 toolkit.

Book an implementation consultant today.