Ad – 728×90
💻 Website Development

Odoo Website Snippets – Building Blocks for the Page Editor

Snippets (building blocks) are the drag-and-drop components that appear in the Odoo website editor's block palette. You can create custom snippets by registering a template with the right XML structure, and add options that let non-developers configure them visually.

⏱️ 25 min 🎯 Advanced 📅 Updated 2026
What you'll learn:
  • Snippet XML structure and how to register a building block
  • Making zones editable with oe_structure
  • Adding snippet options (color pickers, layout selectors)
  • Dynamic snippets that load data from Odoo models
  • JavaScript snippet initialization

Snippet XML Structure

A snippet consists of two parts: the snippet template itself, and an entry in the snippets panel. Both live in the same XML file, typically views/snippets/:

XML
<!-- 1. The snippet HTML template -->
<template id="s_book_highlight" name="Book Highlight">
  <section class="s_book_highlight pt-5 pb-5">
    <div class="container">
      <div class="row align-items-center">
        <div class="col-lg-6">
          <h2>Featured Book</h2>
          <p class="lead">Discover our top pick this month.</p>
          <a href="/books" class="btn btn-primary">Browse Library</a>
        </div>
        <div class="col-lg-6">
          <img src="/web/image/library.book/1/cover_image"
               class="img-fluid rounded" alt="Featured Book"/>
        </div>
      </div>
    </div>
  </section>
</template>

<!-- 2. Register it in the snippets panel -->
<template id="snippets" inherit_id="website.snippets">
  <xpath expr="//div[@id='snippet_content']" position="before">
    <t t-snippet="my_module.s_book_highlight"
       t-thumbnail="/my_module/static/description/book_highlight_thumb.png"/>
  </xpath>
</template>

The thumbnail image (160×120px recommended) shows in the editor's block palette. Store it in your module's static/description/ folder.

oe_structure – Editable Zones

Areas where editors can drag more snippets are marked with the oe_structure class. Add an id attribute to persist changes to the database:

XML
<section class="s_book_highlight">
  <div class="container">
    <!-- Editable zone: editors can drag snippets here -->
    <div class="oe_structure oe_empty" id="oe_structure_book_highlight_1"/>
  </div>
</section>

The oe_empty class shows a placeholder message ("Drag building blocks here") when the zone is empty. When an editor drops a snippet here and saves, Odoo stores the HTML in an ir.ui.view record keyed to this id.

Snippet Options

Options let editors configure snippet appearance from the sidebar without editing HTML. Register options by targeting the snippet's CSS class:

XML
<template id="s_book_highlight_options" inherit_id="website.snippet_options">
  <xpath expr="." position="inside">
    <div data-js="BookHighlight" data-selector=".s_book_highlight">
      <!-- Background style option -->
      <we-select string="Background">
        <we-button data-select-class="bg-light">Light</we-button>
        <we-button data-select-class="bg-dark text-white">Dark</we-button>
        <we-button data-select-class="bg-primary text-white">Primary</we-button>
      </we-select>

      <!-- Layout option -->
      <we-select string="Layout">
        <we-button data-select-class="flex-row">Image Right</we-button>
        <we-button data-select-class="flex-row-reverse">Image Left</we-button>
      </we-select>
    </div>
  </xpath>
</template>

data-select-class adds/removes CSS classes on the snippet root element when an option is selected. No JavaScript needed for class-based options.

Ad – 728×90

Dynamic Snippets

Dynamic snippets fetch data from Odoo models and render it in a carousel, grid, or list. They use a JSON route to reload data based on options the editor selects:

Python
class LibrarySnippets(http.Controller):

    @http.route('/books/snippet/featured',
                type='json', auth='public', website=True)
    def featured_books(self, limit=4, **kw):
        books = request.env['library.book'].sudo().search([
            ('website_published', '=', True),
            ('featured', '=', True),
        ], limit=limit)
        return request.env['ir.ui.view']._render_template(
            'my_module.s_featured_books_content',
            {'books': books},
        )

The snippet's JavaScript calls this route when it initializes and when editor options change. Built-in dynamic snippets like s_dynamic_snippet follow this same pattern.

JavaScript Snippet Initialization

For client-side behavior (carousels, counters, animations), add a JavaScript file in static/src/js/ and register a snippet widget:

JavaScript
/** @odoo-module **/
import { SnippetOption } from '@web_editor/js/editor/snippets.options';
import { registry } from "@web/core/registry";

// Public widget — runs on the frontend for visitors
import publicWidget from '@web/legacy/js/public/public_widget';

publicWidget.registry.BookHighlight = publicWidget.Widget.extend({
    selector: '.s_book_highlight',

    start() {
        // Runs when snippet loads on the page
        this._initCounter();
        return this._super(...arguments);
    },

    _initCounter() {
        const counter = this.el.querySelector('.book-count');
        if (counter) {
            // Animate the counter
            const target = parseInt(counter.dataset.target, 10);
            counter.textContent = target.toLocaleString();
        }
    },
});
Key takeaways:
  • A snippet needs two XML records: the template itself and an entry in website.snippets
  • Use oe_structure with a unique id to create editable zones within your snippet
  • Snippet options use we-select / we-button with data-select-class for class-based configuration
  • Dynamic snippets fetch model data via JSON routes and re-render when options change

Frequently Asked Questions

How do I prevent editors from moving or deleting my snippet?

Add data-oe-protected="true" to the snippet root element. This prevents the website editor from moving, duplicating, or removing it. Useful for structural snippets that are part of a page template rather than content blocks.

Where does Odoo store edits made to a page in the editor?

Each page is an ir.ui.view record with a unique key. When an editor saves, Odoo creates a "cow" (copy-on-write) view that overrides the original template for the current website. The original template is preserved and the override is stored as a child view with website_id set.

Can I make snippet text content editable inline?

Yes — add data-oe-model, data-oe-id, and data-oe-field attributes to an element. Odoo's editor will allow inline editing and save changes back to the model field. For static text (not linked to a model), just add the oe_editable class to enable inline text editing that saves to the view record.