Notifal 3.0.0 lets plugins add reusable items to the HTML Builder Widgets palette.
A widget is an HTML shortcut, not a separate frontend component. Clicking or dragging it inserts default_html. The saved HTML remains the source of truth, and the root element's data-notifal-widget attribute lets the builder recognize the widget later.
Hook reference: Hooks: Templates and Rendering.
Recommended integration: PHP filter
Use FilterHooks::TEMPLATE_HTML_BUILDER_WIDGETS, which maps to:
notifal/template/html_builder/widgets
PHP registration is recommended because Notifal sends the same definition to:
- The HTML Builder widget palette
- Noti (hosted AI) in the HTML Builder
- The OnPage notification AI wizard
Widget definition fields
Every field must be a non-empty string.
| Key | Purpose |
|---|---|
id | Unique stable identifier; should use lowercase letters and hyphens |
label | Translated name shown in the palette |
category | Stable category key, such as basic, media, actions, or layout |
category_label | Translated category heading |
icon | Notifal icon name shown on the widget card |
description | Short translated explanation |
default_html | HTML inserted by click or drag |
ai_snippet | HTML structure supplied to AI prompts |
The root element in both HTML fields should use:
data-notifal-widget="your-widget-id"
The attribute value must exactly match id.
Complete plugin example
<?php
/**
* Plugin Name: My Notifal HTML Builder Widgets
* Description: Registers a reusable promo badge in the Notifal HTML Builder.
* Version: 1.0.0
* Requires PHP: 7.4
*/
defined( 'ABSPATH' ) || exit;
use Notifal\Infrastructure\WordPress\Hooks\ActionHooks;
use Notifal\Infrastructure\WordPress\Hooks\FilterHooks;
/**
* Attach the extension after all plugins have loaded.
*
* @return void
*/
function my_notifal_bootstrap_html_builder_widgets(): void {
// Stop when the base plugin or its hook constants are unavailable.
if ( ! class_exists( ActionHooks::class ) || ! class_exists( FilterHooks::class ) ) {
return;
}
// Wait until Notifal has initialized all base services.
add_action(
ActionHooks::PLUGIN_INIT,
'my_notifal_register_html_builder_widgets'
);
}
// Run after WordPress has loaded every active plugin.
add_action(
'plugins_loaded',
'my_notifal_bootstrap_html_builder_widgets',
20
);
/**
* Register the HTML Builder widget filter.
*
* @return void
*/
function my_notifal_register_html_builder_widgets(): void {
// Attach the palette filter before the HTML Builder assets are prepared.
add_filter(
FilterHooks::TEMPLATE_HTML_BUILDER_WIDGETS,
'my_notifal_add_promo_badge_widget'
);
}
/**
* Add a promo badge widget to the HTML Builder.
*
* @param array<int, array<string, string>> $widgets Existing widget definitions.
* @return array<int, array<string, string>> Filtered widget definitions.
*/
function my_notifal_add_promo_badge_widget( array $widgets ): array {
// Keep inserted HTML self-contained and use a namespaced class.
$widget_html = '<span data-notifal-widget="promo-badge" class="my-notifal-promo-badge">New</span>';
// Append a complete definition; incomplete rows are discarded by Notifal.
$widgets[] = [
'id' => 'promo-badge',
'label' => __( 'Promo Badge', 'my-notifal-widgets' ),
'category' => 'basic',
'category_label' => __( 'Basic', 'my-notifal-widgets' ),
'icon' => 'layers',
'description' => __( 'Add a small promotional badge.', 'my-notifal-widgets' ),
'default_html' => $widget_html,
'ai_snippet' => $widget_html,
];
// Always return the complete widget list.
return $widgets;
}
notifal/initialized.Add widget styles
The widget inserts HTML only. Add its CSS from your plugin using normal WordPress enqueue APIs.
Use a namespaced class to prevent conflicts:
.my-notifal-promo-badge {
display: inline-flex;
padding: 4px 8px;
border-radius: 999px;
background: #7e2bd2;
color: #ffffff;
font-size: 12px;
font-weight: 700;
}
For production, enqueue your built stylesheet. Do not print inline <style> or <script> blocks from the registration callback.
If your widget must be portable inside exported template HTML, include the relevant CSS in the template's existing <style> block instead of relying on a site-specific plugin asset.
Write a useful AI snippet
ai_snippet teaches AI tools how and when to output the widget's structure.
Guidelines:
- Keep the widget ID and required classes identical to
default_html. - Include required data attributes.
- Use realistic placeholder copy or supported Notifal tags.
- Do not include secrets, site-specific IDs, nonces, or private URLs.
- Prefer semantic HTML for readable text (
p,span, headings, links, and buttons).
The AI may choose free HTML when your custom widget does not fit the requested design.
Optional JavaScript filter
The client registry also applies:
notifal.htmlBuilder.widgets
through wp.hooks.
/**
* Add a client-only widget definition.
*
* @param {Array<Object>} widgets Existing HTML Builder widgets.
* @returns {Array<Object>} Updated widget list.
*/
const addPromoBadgeWidget = (widgets) => {
// Append a complete definition matching the PHP schema.
widgets.push({
id: 'promo-badge',
label: 'Promo Badge',
category: 'basic',
category_label: 'Basic',
icon: 'layers',
description: 'Add a small promotional badge.',
default_html:
'<span data-notifal-widget="promo-badge" class="my-notifal-promo-badge">New</span>',
ai_snippet:
'<span data-notifal-widget="promo-badge" class="my-notifal-promo-badge">New</span>',
});
// Return the same array after adding the widget.
return widgets;
};
// Register before the HTML Builder React application resolves its widget list.
window.wp.hooks.addFilter(
'notifal.htmlBuilder.widgets',
'my-notifal-widgets/promo-badge',
addPromoBadgeWidget
);
Use the PHP filter when possible. A client-only widget is available in that browser session, but its definition is not automatically included in server-generated AI prompt configuration.
Your script must load on the HTML Builder screen before the React application initializes. The builder declares WordPress wp-hooks as a dependency.
Sanitization and security
Notifal sanitizes the resulting template when it is saved.
data-notifal-widgetis preserved for supported HTML elements.- Inline
on*event handlers are removed. javascript:URLs are rejected.- PHP code is removed.
- Script permissions follow the user's
unfiltered_htmlcapability.
Treat widget HTML as output:
- Escape dynamic URLs with
esc_url(). - Escape text with
esc_html(). - Escape attribute values with
esc_attr(). - Do not put request data directly into
default_html. - Do not use the widget registration filter to process form submissions.
User guide: HTML Sanitization and Allowed HTML.
Modify or remove a widget
Definitions are ordinary array rows. Match by id.
/**
* Remove the core spacer widget.
*
* @param array<int, array<string, string>> $widgets Existing widgets.
* @return array<int, array<string, string>> Widgets without the spacer.
*/
function my_notifal_remove_spacer_widget( array $widgets ): array {
// Keep rows whose stable ID is not spacer.
return array_values(
array_filter(
$widgets,
static function ( array $widget ): bool {
// Read the ID defensively before comparing it.
$widget_id = isset( $widget['id'] )
? sanitize_key( (string) $widget['id'] )
: '';
// Return false only for the core spacer row.
return 'spacer' !== $widget_id;
}
)
);
}
Removing a definition does not remove previously inserted HTML. Existing templates remain normal HTML; they simply stop showing that widget badge if its identifying structure is changed.
Troubleshooting
| Problem | Cause or fix |
|---|---|
| Widget does not appear | Confirm all eight fields are non-empty strings |
| Widget appears in the wrong group | Use the same category key and category_label as the intended group |
| Widget inserts but has no badge | Put matching data-notifal-widget on the root element |
| Widget is missing from AI guidance | Register it through PHP and provide a non-empty ai_snippet |
| Markup changes after save | Review HTML Builder sanitization rules and the current user's capability |
| JavaScript widget does not appear | Load the filter script before the HTML Builder app initializes |
What to read next
Cookbook series
- Cookbook: Modify Notification Before Display
- Cookbook: Add Custom Dynamic Tag
- Cookbook: Customize Template Import
- Cookbook: Extend Display Rules
- Cookbook: Register a Custom HTML Builder Widget (you are here)