Elementor Widget Generator
Generate an Elementor widget class: the name, icon and category, a content section of controls, the render method and the registration hook.
<?php
/**
* The "Card" Elementor widget.
*/
defined( 'ABSPATH' ) || exit;
/**
* Elementor loads this file only when Elementor itself is running, so the
* parent class is always available by the time the class is declared.
*/
class My_Plugin_Card_Widget extends \Elementor\Widget_Base {
/**
* The name stored in the page. Changing it breaks existing pages.
*
* @return string
*/
public function get_name() {
return 'my_plugin_card';
}
/**
* The label in the panel.
*
* @return string
*/
public function get_title() {
return esc_html__( 'Card', 'my-plugin' );
}
/**
* The panel icon.
*
* @return string
*/
public function get_icon() {
return 'eicon-post-info';
}
/**
* Which panel section the widget appears in.
*
* @return array
*/
public function get_categories() {
return array( 'general' );
}
/**
* Declares the controls.
*/
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Content', 'my-plugin' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
)
);
$this->add_control(
'heading',
array(
'label' => esc_html__( 'Heading', 'my-plugin' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => 'Card title',
)
);
$this->add_control(
'body',
array(
'label' => esc_html__( 'Body text', 'my-plugin' ),
'type' => \Elementor\Controls_Manager::TEXTAREA,
'rows' => 4,
'default' => 'A short description.',
)
);
$this->add_control(
'show_link',
array(
'label' => esc_html__( 'Show the link', 'my-plugin' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'my-plugin' ),
'label_off' => esc_html__( 'No', 'my-plugin' ),
'return_value' => 'yes',
'default' => 'yes',
)
);
$this->end_controls_section();
}
/**
* Prints the widget on the front end.
*
* Settings come back exactly as they were typed, so escape on output.
*/
protected function render() {
$settings = $this->get_settings_for_display();
echo '<div class="' . esc_attr( 'my-plugin-card' ) . '">';
if ( ! empty( $settings['heading'] ) ) {
echo '<span class="' . esc_attr( 'my-plugin-card__heading' ) . '">' . esc_html( $settings['heading'] ) . '</span>';
}
if ( ! empty( $settings['body'] ) ) {
echo '<p>' . esc_html( $settings['body'] ) . '</p>';
}
// show_link is a switcher: compare against 'yes'.
echo '</div>';
}
}
/**
* Registers the widget.
*
* @param \Elementor\Widgets_Manager $widgets_manager Elementor's manager.
*/
function my_plugin_register_widget( $widgets_manager ) {
$widgets_manager->register( new My_Plugin_Card_Widget() );
}
add_action( 'elementor/widgets/register', 'my_plugin_register_widget' );
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Describe the widget and its panel controls, and the generator writes the class: the name Elementor stores, the controls section, an escaped render method and the registration hook.
How to use
- Pick the widget name carefully. Elementor writes it into every page that uses the widget, so renaming it later leaves those pages with a missing widget.
- Prefix the class name. Widget classes are global, and two add-ons with a
Card_Widgetfatal on the second one. - Add one control per setting. The control ID becomes the key in
$settings, so keep them lowercase and stable. - Register a custom category when the add-on ships more than one widget. Users look for a named group, not for your widget among a hundred others.
- Load the file only when Elementor is running, and require it from inside the registration hook rather than at the top of your plugin.
Example
The registration, which changed in Elementor 3.5:
function acme_register_widget( $widgets_manager ) {
$widgets_manager->register( new Acme_Card_Widget() );
}
add_action( 'elementor/widgets/register', 'acme_register_widget' );
The old widgets_registered action with register_widget_type() still works through a deprecation shim, but it is on its way out and warns in the log.
Pitfalls
- Declaring the class at the top of your plugin file fatals when Elementor is not active, because the parent class does not exist. Require it inside the hook.
get_name()is stored in page content. Changing it turns every existing instance into a missing widget.- Settings are returned exactly as typed. Escaping happens in
render(), and forgetting it is a stored XSS in a page builder. - The
URLcontrol returns an array withurl,is_externalandnofollow, not a string. Treating it as a string printsArray. - A
SWITCHERreturns the stringyesor an empty string. Comparing it totruenever matches. render()prints; it does not return. Returning the markup gives you an empty widget.- Widgets need a matching
content_template()to preview live in the editor. Without one the editor re-renders through PHP, which is slower but correct. - Icons must be loaded in the editor. An
eicon-class always works; a Font Awesome class only works if your add-on enqueues it there.
Compatibility
\Elementor\Widget_Base, start_controls_section() and add_control() have been stable since Elementor 1.x. elementor/widgets/register with $widgets_manager->register() needs Elementor 3.5 or later; older versions used elementor/widgets/widgets_registered and register_widget_type(). get_settings_for_display() needs 2.0. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.
Frequently asked questions
Where do I put the file?
elementor/widgets/register callback. That way nothing loads when Elementor is not active.Why does my widget not appear in the panel?
How do I add style controls?
tab argument is Controls_Manager::TAB_STYLE, and put the appearance controls in it.