Powerbox Editors in Odoo 18
By Braincuber Team
Published on January 3, 2026
Developers spend hours building custom text formatting tools: users request inline data insertion, team mentions, quick commands—but traditional form fields provide no context-aware functionality. Users switch between fields, open popups, navigate menus just to insert a link, add a template, or reference another record. The typing workflow breaks constantly, destroying productivity and frustrating users who expect modern text interaction patterns from platforms like Slack, Notion, or Discord.
Odoo 18 introduces Powerbox Editors, a slash-command system that transforms rich text fields into intelligent input interfaces. Users type / to reveal contextual command menus, insert dynamic content blocks, mention team members, embed templates, or execute custom actions—all without leaving the text editor. Build custom Powerbox plugins that extend editor functionality with your own commands, icons, categories, and content insertion logic, creating seamless user experiences matching modern productivity applications.
Core Benefit: Powerbox Editors = slash commands in Odoo. Type / in any rich text field → contextual menu appears → select command → content inserted instantly. Build custom plugins to add your own commands like /document, /template, /task, /mention.
Understanding Powerbox Editors
Slash Command Interface
Users type / while editing text to trigger contextual command menu. Menu displays available commands grouped by category, allowing quick access to editor tools without leaving the typing area.
Plugin Architecture
Extend the editor by creating custom plugins. Each plugin registers commands with unique IDs, titles, descriptions, and icons. Commands execute functions that insert content or perform actions.
Dynamic Content Insertion
Insert complex content blocks: documents, templates, embeds, tables, media. Content renders from XML templates, enabling rich formatting and interactive elements within the editor.
Undo/Redo Support
Plugins integrate with editor history system. Every content insertion logs as a step, enabling users to undo/redo changes. Maintains clean editing experience with full revision history.
Powerbox Activation
When users edit rich text fields in Odoo 18, typing a forward slash (/) activates the Powerbox. The contextual menu displays available commands that match the typed text.
User Interaction Flow:
USER TYPING IN RICH TEXT FIELD: Step 1: User types text normally "Here is the project documentation..." Step 2: User types "/" to activate Powerbox "Here is the project documentation... /" ↓ ┌─────────────────────────────────────┐ │ POWERBOX MENU │ ├─────────────────────────────────────┤ │ 📋 Knowledge │ │ 📄 Document - Insert Documents │ │ 📁 Template - Insert Template │ │ │ │ 📊 Structure │ │ 📝 Table - Insert Table │ │ 🔗 Link - Insert Link │ │ │ │ 👥 Collaboration │ │ @ Mention - Tag a user │ │ 📌 Task - Create linked task │ └─────────────────────────────────────┘ Step 3: User types to filter or clicks option "/doc" → Filters to "Document" command Step 4: User selects command → Plugin executes insertArticleDocument() → Document template renders in editor → Step logged for undo/redo RESULT: "Here is the project documentation... ┌─────────────────────────────────────┐ │ 📄 DOCUMENT BLOCK │ │ [Your content here...] │ └─────────────────────────────────────┘ "
Plugin Architecture Overview
Powerbox plugins extend the Odoo HTML editor by registering custom commands. Each plugin defines its identity, dependencies, resources (commands and powerbox items), and execution methods.
Plugin Structure:
POWERBOX PLUGIN ANATOMY
class CustomPlugin extends Plugin {
// 1. IDENTITY
static id = "uniquePluginId"; // Unique identifier
// 2. DEPENDENCIES
static dependencies = [ // Required editor services
"history", // For undo/redo support
"dom" // For DOM manipulation
];
// 3. RESOURCES
resources = {
// User commands (what appears in menu)
user_commands: [
{
id: "commandName", // Unique command ID
title: _t("Display Name"),// Shown in menu
description: _t("Help"), // Description tooltip
icon: "fa-icon-class", // FontAwesome icon
run: this.method.bind(this) // Execution function
}
],
// Powerbox item registration
powerbox_items: [
{
categoryId: "categoryName", // Menu category
commandId: "commandName" // Links to user_command
}
]
};
// 4. EXECUTION METHOD
executeMethod() {
// Insert content using DOM service
const element = renderToElement("template.name");
this.dependencies.dom.insert(element);
// Log step for undo/redo
this.dependencies.history.addStep();
}
}
Creating a Custom Powerbox Plugin
Build a complete Powerbox plugin that adds a "Document" command to the Knowledge editor. This example demonstrates the full implementation pattern.
Create the JavaScript Plugin File
Create a new JavaScript file in your module's static folder:
- Path:
your_module/static/src/js/article_document_plugin.js - Import required modules from Odoo core
- Define plugin class extending base Plugin
- Register with the editor's plugin set
Complete Plugin Implementation:
/** @odoo-module **/
import { Plugin } from "@html_editor/plugin";
import { _t } from "@web/core/l10n/translation";
import { renderToElement } from "@web/core/utils/render";
import { KNOWLEDGE_PLUGINS } from "@knowledge/editor/plugin_sets";
/**
* ArticleDocumentPlugin
* Adds a "Document" command to the Knowledge editor Powerbox.
* When invoked, inserts an editable document block into the article.
*/
export class ArticleDocumentPlugin extends Plugin {
// Unique identifier for this plugin
static id = "articleDocument";
// Required editor services
static dependencies = ["history", "dom"];
// Plugin resources: commands and powerbox items
resources = {
// Define the user command
user_commands: [
{
id: "insertArticleDocument",
title: _t("Document"),
description: _t("Insert an editable document block"),
icon: "fa-file-text-o",
run: this.insertArticleDocument.bind(this),
},
],
// Register command in Powerbox under "knowledge" category
powerbox_items: [
{
categoryId: "knowledge",
commandId: "insertArticleDocument",
}
],
};
/**
* Insert the document template into the editor.
* Renders the XML template and inserts at cursor position.
*/
insertArticleDocument() {
// Render the QWeb template to a DOM element
const articleDocumentBlueprint = renderToElement(
"your_module.ArticleDocumentBlueprint"
);
// Insert the element at cursor position using DOM service
this.dependencies.dom.insert(articleDocumentBlueprint);
// Log this action for undo/redo history
this.dependencies.history.addStep();
}
}
// Register the plugin with Knowledge editor
KNOWLEDGE_PLUGINS.push(ArticleDocumentPlugin);
Create the XML Template
Define the content template that renders when command executes:
- Path:
your_module/static/src/xml/document_templates.xml - Use QWeb template syntax
- Set
contenteditable="true"for inline editing - Add
data-embeddedattribute for embedded component handling
Template XML Definition:
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<!-- Basic Document Block Template -->
<t t-name="your_module.ArticleDocumentBlueprint">
<div class="o_knowledge_document_block"
data-embedded="DocumentContent"
contenteditable="true">
<div class="document-header">
<span class="document-icon">
<i class="fa fa-file-text-o"></i>
</span>
<span class="document-title">New Document</span>
</div>
<div class="document-content">
<p>Enter your document content here...</p>
</div>
</div>
</t>
<!-- Task Reference Block Template -->
<t t-name="your_module.TaskReferenceBlueprint">
<div class="o_knowledge_task_block"
data-embedded="TaskReference"
contenteditable="false">
<div class="task-badge">
<i class="fa fa-tasks"></i>
<span>Linked Task</span>
</div>
<div class="task-placeholder">
Click to select a task...
</div>
</div>
</t>
<!-- Code Snippet Block Template -->
<t t-name="your_module.CodeSnippetBlueprint">
<div class="o_knowledge_code_block"
data-embedded="CodeSnippet">
<div class="code-header">
<span class="code-language">JavaScript</span>
<button class="code-copy-btn">Copy</button>
</div>
<pre class="code-content" contenteditable="true">
// Your code here...
</pre>
</div>
</t>
</templates>
Register Assets in Manifest
Include JavaScript and XML files in module manifest:
- Add JS to appropriate asset bundle (usually
web.assets_backend) - Include XML templates in same bundle
- Ensure dependencies include
knowledgemodule
Module Manifest Configuration:
{
'name': 'Knowledge Document Plugin',
'version': '18.0.1.0.0',
'category': 'Productivity/Knowledge',
'summary': 'Add custom document blocks to Knowledge articles',
'description': """
This module extends the Knowledge editor with a custom
Powerbox command for inserting document blocks.
Features:
- /document command in Powerbox
- Editable document block insertion
- Undo/redo support
- Custom styling
""",
'author': 'Your Company',
'website': 'https://yourcompany.com',
'depends': ['knowledge', 'web'],
'data': [],
'assets': {
'web.assets_backend': [
# JavaScript plugin
'your_module/static/src/js/article_document_plugin.js',
# XML templates
'your_module/static/src/xml/document_templates.xml',
# Optional: Custom CSS styling
'your_module/static/src/css/document_blocks.css',
],
},
'installable': True,
'application': False,
'license': 'LGPL-3',
}
Add Custom Styling (Optional)
Create CSS file to style your document blocks:
- Path:
your_module/static/src/css/document_blocks.css - Style the container, header, and content areas
- Add hover effects and visual feedback
- Ensure responsive design for different screen sizes
Custom Block Styling:
/* Document Block Styling */
.o_knowledge_document_block {
border: 1px solid #e2e8f0;
border-radius: 8px;
margin: 16px 0;
background: linear-gradient(135deg, #f8f9fc 0%, #fff 100%);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
}
.o_knowledge_document_block:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
}
.document-header {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e2e8f0;
background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
}
.document-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 6px;
color: white;
margin-right: 12px;
}
.document-title {
font-weight: 600;
color: #2d3748;
font-size: 14px;
}
.document-content {
padding: 16px;
min-height: 100px;
}
.document-content p {
color: #718096;
font-size: 14px;
line-height: 1.6;
margin: 0;
}
/* Code Block Styling */
.o_knowledge_code_block {
border: 1px solid #4a5568;
border-radius: 8px;
margin: 16px 0;
overflow: hidden;
}
.code-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.code-language {
color: white;
font-weight: 600;
font-size: 12px;
text-transform: uppercase;
}
.code-copy-btn {
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.code-copy-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.code-content {
background: #2d3748;
padding: 16px;
margin: 0;
color: #e2e8f0;
font-family: Monaco, Menlo, monospace;
font-size: 13px;
line-height: 1.5;
overflow-x: auto;
}
/* Task Block Styling */
.o_knowledge_task_block {
border: 1px dashed #e2e8f0;
border-radius: 8px;
padding: 16px;
margin: 16px 0;
background: #fafafa;
cursor: pointer;
transition: all 0.2s ease;
}
.o_knowledge_task_block:hover {
border-color: #667eea;
background: #f0f4ff;
}
.task-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
margin-bottom: 12px;
}
.task-placeholder {
color: #a0aec0;
font-size: 14px;
}
Advanced Plugin Examples
Extend your Powerbox with multiple commands. Create a comprehensive plugin set for common content types.
Multi-Command Plugin Example:
/** @odoo-module **/
import { Plugin } from "@html_editor/plugin";
import { _t } from "@web/core/l10n/translation";
import { renderToElement } from "@web/core/utils/render";
import { KNOWLEDGE_PLUGINS } from "@knowledge/editor/plugin_sets";
/**
* AdvancedContentPlugin
* Provides multiple content block commands for Knowledge editor.
*/
export class AdvancedContentPlugin extends Plugin {
static id = "advancedContent";
static dependencies = ["history", "dom"];
resources = {
user_commands: [
// Document Block Command
{
id: "insertDocument",
title: _t("Document"),
description: _t("Insert an editable document section"),
icon: "fa-file-text-o",
run: this.insertDocument.bind(this),
},
// Code Snippet Command
{
id: "insertCodeSnippet",
title: _t("Code"),
description: _t("Insert a formatted code block"),
icon: "fa-code",
run: this.insertCodeSnippet.bind(this),
},
// Callout Box Command
{
id: "insertCallout",
title: _t("Callout"),
description: _t("Insert an info/warning callout box"),
icon: "fa-info-circle",
run: this.insertCallout.bind(this),
},
// Table of Contents Command
{
id: "insertTOC",
title: _t("Table of Contents"),
description: _t("Insert auto-generated TOC"),
icon: "fa-list-ol",
run: this.insertTableOfContents.bind(this),
},
// Divider Command
{
id: "insertDivider",
title: _t("Divider"),
description: _t("Insert a horizontal divider"),
icon: "fa-minus",
run: this.insertDivider.bind(this),
},
],
powerbox_items: [
// Knowledge Category
{ categoryId: "knowledge", commandId: "insertDocument" },
{ categoryId: "knowledge", commandId: "insertCallout" },
{ categoryId: "knowledge", commandId: "insertTOC" },
// Structure Category
{ categoryId: "structure", commandId: "insertCodeSnippet" },
{ categoryId: "structure", commandId: "insertDivider" },
],
};
insertDocument() {
const element = renderToElement("your_module.DocumentBlueprint");
this.dependencies.dom.insert(element);
this.dependencies.history.addStep();
}
insertCodeSnippet() {
const element = renderToElement("your_module.CodeSnippetBlueprint");
this.dependencies.dom.insert(element);
this.dependencies.history.addStep();
}
insertCallout() {
const element = renderToElement("your_module.CalloutBlueprint");
this.dependencies.dom.insert(element);
this.dependencies.history.addStep();
}
insertTableOfContents() {
const element = renderToElement("your_module.TOCBlueprint");
this.dependencies.dom.insert(element);
this.dependencies.history.addStep();
}
insertDivider() {
const element = renderToElement("your_module.DividerBlueprint");
this.dependencies.dom.insert(element);
this.dependencies.history.addStep();
}
}
KNOWLEDGE_PLUGINS.push(AdvancedContentPlugin);
Plugin Development Best Practices
✅ Follow These Guidelines:
- Use unique plugin IDs: Prefix with your module name to avoid conflicts (e.g.,
yourModule_documentBlock) - Always call addStep(): After inserting content, log the step for undo/redo support—users expect to revert changes
- Use translation function: Wrap all user-facing strings in
_t()for multi-language support - Choose meaningful icons: Select FontAwesome icons that clearly represent the command action
- Write clear descriptions: The description appears in Powerbox menu—help users understand what the command does
- Group related commands: Use appropriate categoryId to organize commands logically in the menu
- Style consistently: Match your block styling to Odoo's design system for seamless integration
- Test contenteditable: Verify users can edit inserted content where expected, and that non-editable blocks behave correctly
Common Mistakes to Avoid
🚨 Mistake #1: Creating Multiple Powerbox Instances
The editor automatically creates a single Powerbox instance stored in its powerbox variable. Never create another instance manually—always extend by registering plugins before editor initialization. Multiple instances cause conflicts and unpredictable behavior.
🚨 Mistake #2: Forgetting History Integration
Omitting this.dependencies.history.addStep() after content insertion breaks undo/redo functionality. Users cannot revert changes, leading to frustration. Always log history steps for any DOM modification.
🚨 Mistake #3: Mismatched Template Names
Template name in renderToElement("module.TemplateName") must exactly match the t-name attribute in XML. Case-sensitive mismatch causes "template not found" errors. Double-check naming consistency between JS and XML files.
🚨 Mistake #4: Missing Asset Registration
JavaScript and XML files must be included in __manifest__.py assets. Forgetting to register files means plugin code never loads. Verify files appear in web.assets_backend bundle after module installation.
Conclusion
Odoo 18 Powerbox Editors transform rich text fields into intelligent command interfaces matching modern productivity applications like Notion and Slack. Users type / to access contextual command menus, instantly inserting documents, templates, code blocks, or custom content without leaving the editor. Build custom plugins by extending the base Plugin class, defining user commands with unique IDs, titles, icons, and execution methods, registering commands in appropriate Powerbox categories, creating QWeb templates for content rendering, and styling blocks to match Odoo's design system—enabling seamless, slash-command-driven content creation that dramatically improves user productivity and editor extensibility for your custom Odoo applications.
🎯 Key Takeaway: Powerbox = slash commands for Odoo. Type / → menu appears → select command → content inserted. Build plugins with user_commands + powerbox_items + execution method + XML template. Modern UX for Odoo text editors.
