---
name: magento2-widget-creation
description: Comprehensive guide for creating custom widget modules in Magento 2 that can be inserted into CMS pages and blocks. Covers module structure, widget configuration, templates, JavaScript, CSS, and form submission handling for non-Hyvä themes.
---
# Magento 2 Widget Creation for CMS Pages
## Purpose
This skill provides comprehensive guidance on creating custom widget modules in Magento 2 (standard Luma/Blank themes, not Hyvä-based) that can be inserted into CMS pages, CMS blocks, or any content area using the widget system.
## When to Use This Skill
Use this skill when you need to:
- Create a reusable component that can be inserted into CMS pages
- Build interactive elements (buttons, forms, modals) for content editors
- Develop custom functionality that non-technical users can add to pages
- Create widgets with configurable parameters that appear in the admin panel
- Implement widgets that work with standard Magento themes (Luma/Blank)
**Do NOT use this skill for:**
- Hyvä theme widgets (use hyva-tailwind-integration skill instead)
- Backend admin widgets
- UI components or admin grids
## Prerequisites
- Existing vendor namespace or willingness to create one
- Basic understanding of Magento 2 module structure
- Knowledge of XML configuration
- Familiarity with Magento templates and blocks
- Understanding of JavaScript widget pattern (optional, for interactive widgets)
## Widget Module Structure
A complete widget module requires these components:
```
app/code/Vendor/ModuleName/
├── registration.php # Module registration
├── etc/
│ ├── module.xml # Module configuration
│ ├── widget.xml # Widget definition
│ ├── email_templates.xml # (Optional) Email templates
│ └── frontend/
│ └── routes.xml # (Optional) For form submissions
├── Block/
│ └── Widget/
│ └── WidgetName.php # Widget block class
├── Controller/ # (Optional) For form handlers
│ └── Index/
│ └── Submit.php
└── view/frontend/
├── templates/
│ └── widget/
│ └── template.phtml # Widget template
├── layout/
│ └── default.xml # (Optional) Load CSS/JS globally
├── web/
│ ├── js/
│ │ └── widget-script.js # (Optional) Custom JS
│ └── css/
│ └── widget-style.css # (Optional) Custom CSS
├── requirejs-config.js # (Optional) JS module mapping
└── email/ # (Optional) Email templates
└── template.html
```
## Step-by-Step Widget Creation
### Step 1: Create Module Registration
**File: `registration.php`**
```php
```
**Key Points:**
- `setup_version` is legacy but still commonly used
- Add dependencies in `` - `Magento_Cms` and `Magento_Widget` are required for widgets
- Add other modules your widget depends on (e.g., `Magento_Email`, `Magento_Catalog`)
### Step 3: Create Widget Configuration
**File: `etc/widget.xml`**
This is where you define your widget's metadata and configurable parameters.
```xml
Brief description of what the widget doesDescription shown in adminSelect Block...
```
**Widget Parameter Types:**
- `text` - Simple text input
- `select` - Dropdown selection
- `multiselect` - Multiple selections
- `block` - CMS block picker
- `page` - CMS page picker
- `conditions` - Product/category conditions (advanced)
**Important Attributes:**
- `id` - Unique identifier for the widget
- `class` - Full namespaced path to your block class
- `required` - Whether parameter is mandatory
- `visible` - Whether parameter shows in admin
### Step 4: Create Block Class
**File: `Block/Widget/WidgetName.php`**
The block class handles the widget's logic and data.
```php
getData('text_param') ?: 'default value';
}
/**
* Get widget select parameter
*
* @return string
*/
public function getSelectValue(): string
{
return $this->getData('select_param') ?: 'value1';
}
/**
* Check if feature is enabled
*
* @return bool
*/
public function isEnabled(): bool
{
return (bool)$this->getData('enabled');
}
/**
* Get URL for AJAX or form submission
*
* @return string
*/
public function getActionUrl(): string
{
return $this->getUrl('modulename/index/submit');
}
}
```
**Best Practices:**
- Always `declare(strict_types=1);`
- Implement `BlockInterface`
- Use `$this->getData('param_name')` to access widget parameters
- Provide default values with `?:` operator
- Add type hints and return types
- Keep business logic out of templates - put it in block methods
### Step 5: Create Template File
**File: `view/frontend/templates/widget/template.phtml`**
Templates render the HTML output. Always escape data for security.
```php
getParameterValue();
$selectValue = $block->getSelectValue();
$isEnabled = $block->isEnabled();
$uniqueId = uniqid('widget_');
?>
= $escaper->escapeHtml(__('Widget Title')) ?>
= $escaper->escapeHtml($paramValue) ?>
```
**Template Best Practices:**
- Always use `$escaper->escapeHtml()` for text content
- Use `$escaper->escapeHtmlAttr()` for HTML attributes
- Use `$escaper->escapeJs()` for JavaScript strings
- Use `$escaper->escapeUrl()` for URLs
- Use `__()` for translatable strings
- Generate unique IDs to avoid conflicts (use `uniqid()`)
- Add proper `@var` comments for IDE support
**Common Escaping Methods:**
```php
// Text content
= $escaper->escapeHtml($text) ?>
// HTML attributes