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.

Live output

Enable JavaScript to customise; default output below.

Prefixed, because widget classes share the global namespace with every other add-on.

Saved into every page that uses the widget. Renaming it later breaks those pages.

An eicon name, or a Font Awesome class if your add-on loads it in the editor.

custom registers a category of your own, which is how add-ons group their widgets.

Live preview elementor-widget.php
<?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.

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

  1. 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.
  2. Prefix the class name. Widget classes are global, and two add-ons with a Card_Widget fatal on the second one.
  3. Add one control per setting. The control ID becomes the key in $settings, so keep them lowercase and stable.
  4. 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.
  5. 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 URL control returns an array with url, is_external and nofollow, not a string. Treating it as a string prints Array.
  • A SWITCHER returns the string yes or an empty string. Comparing it to true never 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?
In your own plugin, required from inside the elementor/widgets/register callback. That way nothing loads when Elementor is not active.
Why does my widget not appear in the panel?
Usually the category. A widget in a category that was never registered is not shown anywhere.
How do I add style controls?
Add a second section whose tab argument is Controls_Manager::TAB_STYLE, and put the appearance controls in it.
Do I need content_template()?
Only for live preview in the editor. Without it the editor falls back to rendering through PHP, which still works.
Can I use this with Elementor Pro widgets?
Yes. Pro adds categories and controls; the base class and the registration hook are the same.

From the people who built this tool

Master Addons for Elementor

The widgets Elementor leaves out, built to stay fast: advanced tabs and accordions, mega menus, tables, forms and the extensions around them.

See Master Addons Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.