Creating your own custom Elementor widgets

For those who don’t know, Elementor is an amazing page builder for WordPress. Super fast with some really nifty features. Check it out here.

Today I’ll run through how you can add your own custom elements into the sidebar. We’ll do a basic one that outputs some text along with some recent post titles.

This is what the new element will look like:

 

And this is what will happen when dragging this element onto the page:

 

I’ll wrap this up into a handy plugin at the end, nice copy-paste example for you to use.

Elementor does not have a developer API as such, so we’ll just be interacting with the Elementor function calls directly.

 

Step 1: Choose an Elementor icon

Elementor widgets look like this:

So we need to choose an icon for the new one we’re going to make. The easiest way is to pick one of the existing Elementor icons (you can use font-awesome, it’s just a little messy, I’ll cover that in another post).

Head over to http://bluejamesbond.github.io/CharacterMap/ and click Select Font then upload the wp-content/plugins/elementor/assets/lib/eicons/fonts/eicons.ttf file. You’ll see all the available icons:

I click on the icon I wish to use and take note of the “name” to the left. The one I’m using here is called “post-list”:

 

 

Step 2: Create a WordPress plugin to hold our Elementor widget

Here’s the code. Nothing special. It just hooks into elementor/widgets/widgets_registered and then includes our Elementor file. We do it this way so the theme can override this custom Elementor widget if it wants to (I do things this way so I can share a base plugin between all my themes and make individual theme tweaks if necessary).

 

<?php

/**
 * Plugin Name: Elementor Custom Elements
 * Description: Custom element added to Elementor
 * Plugin URI: http://dtbaker.net/web-development/creating-your-own-custom-elementor-widgets/
 * Version: 0.0.1
 * Author: dtbaker
 * Author URI: http://dtbaker.net
 * Text Domain: elementor-custom-element
 */

if ( ! defined( 'ABSPATH' ) ) exit;

// This file is pretty much a boilerplate WordPress plugin.
// It does very little except including wp-widget.php

class ElementorCustomElement {

   private static $instance = null;

   public static function get_instance() {
      if ( ! self::$instance )
         self::$instance = new self;
      return self::$instance;
   }

   public function init(){
      add_action( 'elementor/widgets/widgets_registered', array( $this, 'widgets_registered' ) );
   }

   public function widgets_registered() {

      // We check if the Elementor plugin has been installed / activated.
      if(defined('ELEMENTOR_PATH') && class_exists('Elementor\Widget_Base')){

         // We look for any theme overrides for this custom Elementor element.
         // If no theme overrides are found we use the default one in this plugin.

         $widget_file = 'plugins/elementor/my-widget.php';
         $template_file = locate_template($widget_file);
         if ( !$template_file || !is_readable( $template_file ) ) {
            $template_file = plugin_dir_path(__FILE__).'my-widget.php';
         }
         if ( $template_file && is_readable( $template_file ) ) {
            require_once $template_file;
         }
      }
   }
}

ElementorCustomElement::get_instance()->init();

 

 

Step 3: Create the custom Elementor widget

Now we create the custom Elementor widget. We put this into a my-widget.php file in the plugin folder. This file can also be placed / modified in the theme folder ‘wp-content/themes/my-theme/plugins/elementor/my-widget.php’

The magic here happens with the $this->add_control() calls. I won’t list all the available controls here. The quickest way is to find an existing widget that you like ( wp-content/plugins/elementor/includes/widgets/*.php ) and just copy/paste the relevant controls in.

In our widget here we’re only having a text input box and a drop down list:

<?php
namespace Elementor;

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class Widget_My_Custom_Elementor_Thing extends Widget_Base {

   public function get_id() {
      return 'my-blog-posts';
   }

   public function get_title() {
      return __( 'My Custom Widget', 'elementor-custom-element' );
   }

   public function get_icon() {
      // Icon name from the Elementor font file, as per http://dtbaker.net/web-development/creating-your-own-custom-elementor-widgets/
      return 'post-list';
   }

   protected function _register_controls() {

      $this->add_control(
         'section_blog_posts',
         [
            'label' => __( 'Blog Posts', 'elementor-custom-element' ),
            'type' => Controls_Manager::SECTION,
         ]
      );

      $this->add_control(
         'some_text',
         [
            'label' => __( 'Text', 'elementor-custom-element' ),
            'type' => Controls_Manager::TEXT,
            'default' => '',
            'title' => __( 'Enter some text', 'elementor-custom-element' ),
            'section' => 'section_blog_posts',
         ]
      );

      $this->add_control(
         'posts_per_page',
         [
            'label' => __( 'Number of Posts', 'elementor-custom-element' ),
            'type' => Controls_Manager::SELECT,
            'default' => 5,
            'section' => 'section_blog_posts',
            'options' => [
               1 => __( 'One', 'elementor-custom-element' ),
               2 => __( 'Two', 'elementor-custom-element' ),
               5 => __( 'Five', 'elementor-custom-element' ),
               10 => __( 'Ten', 'elementor-custom-element' ),
            ]
         ]
      );

   }

   protected function render( $instance = [] ) {

      // get our input from the widget settings.

      $custom_text = ! empty( $instance['some_text'] ) ? $instance['some_text'] : ' (no text was entered ) ';
      $post_count = ! empty( $instance['posts_per_page'] ) ? (int)$instance['posts_per_page'] : 5;

      ?>

      <h3>My Example Elementor Widget</h3>
      <p>My text was: <?php echo esc_html( $custom_text );?> </p>
      <h3>Some Recent Posts Here:</h3>
      <ul>
         <?php
         $args = array( 'numberposts' => $post_count );
         $recent_posts = wp_get_recent_posts( $args );
         foreach( $recent_posts as $recent ){
            echo '<li><a href="' . esc_url( get_permalink( $recent["ID"] ) ). '">' .   esc_html( $recent["post_title"] ).'</a> </li> ';
         }
         wp_reset_query();
         ?>
      </ul>

      <?php

   }

   protected function content_template() {}

   public function render_plain_content( $instance = [] ) {}

}

Plugin::instance()->widgets_manager->register_widget( 'Elementor\Widget_My_Custom_Elementor_Thing' );

 

Step 4: Profit!

Feel free to try Elementor (and a couple of my basic Elementor addons) in the live Elementor demo available here: http://summerblog.theme-demo.net/

 

Code is on Github

The above code is over on Github, check it out here: https://github.com/dtbaker/elementor-custom-element

 

What’s next?

You can also add new controls to existing elements. For example: this is how you add a new select control to an existing Elementor widget.

27 comments:

These blog posts are soooo helpful. I haven’t found anyone else writing about Elementor development. Thanks man.

I have a question for you…

All of the widgets that come packaged with elementor make use of the global font settings.

But none of the widgets I create seem to inherit those styles. Is there a trick to getting my custom elementor widgets to inherit the global elementor font styles?

Reply

Hi there! Thanks for sharing!
But since one of the latest updates the code should be slightly different.
In the widget class you should have a function like this:
`
public function get_name() {
return ‘my-widget-name’;
}
`
and
Plugin::instance()->widgets_manager->register_widget( ‘ElementorWidget_My_Custom_Elementor_Thing’ );
has been replaced by
Plugin::instance()->widgets_manager->register_widget_type( new Widget_My_Custom_Elementor_Thing() );

Cheers,
Marc.

Reply

Thanks!

Yep there’s been quite a few breaking changes recently. The latest version includes new hooks/filters that should prevent this from breaking in future updates. I’ll fix up this example so it works with the latest and greatest version shortly.

Cheers.

Reply

Hi dtbaker,
Thanks for this good tutorial.

I have faced one issue related to the text and blog post limit are not changing when I am updating from admin.
After that I have compare code and found below in widget file.

We should replace

$custom_text = ! empty( $instance[‘some_text’] ) ? $instance[‘some_text’] : ‘ (no text was entered ) ‘;
$post_count = ! empty( $instance[‘posts_per_page’] ) ? (int)$instance[‘posts_per_page’] : 5;

by this

$settings = $this->get_settings();
$custom_text = ! empty( $settings[‘some_text’] ) ? $settings[‘some_text’] : ‘ (no text was entered ) ‘;
$post_count = ! empty( $settings[‘posts_per_page’] ) ? (int)$settings[‘posts_per_page’] : 5;

Means we need to use get_setting(); function for getting value.

Please let me know if I am wrong.

Thanks,
Sunil

Reply

Hi dtbaker Thanks for your tutorial. After reading your tutorial I try to build my own widget
I’m adding some additional widgets to the page builder and want to include custom javascript. I found the API for enqueueing javascript however ran into issues: https://github.com/pojome/elementor/blob/develop/docs/content/hooks/php-hooks.md#frontend-actions

The code works great when I view the page on the front-end, however when I create a new widget in the Elementor editor the javascript doesn’t load.

Is there code to enqueue JS per widget/element? I tried using wp_enqueue_script within the protected function render() {} which worked on the front-end but again didn’t work when viewing using the elementor editor.

Reply

Yes I’ve managed to enqueue js for a custom backend widget before. Cannot remember off the top of my head, I’ll hunt through some code and see if I can find out how I did it.

Reply

Hey Dave, I’ve gone through the above comments and the code and updated everything to make this work on the latest version of Elementor (at time of writing, 1.9.3). I’ve uploaded them to Github and made a pull request to your repo, but in the meantime everyone else can check it out on mine.

https://github.com/bcpurleigh/elementor-custom-element

I have also created a version of the above to create more than one custom widget within the same plugin, which I will be adding on to this soon.

Have a great day!

Reply

Hello,

First of al thanks for the GitHub snippet 🙂 Unfortunately if I enable the plugin the rest of the Elementor Pro controls disappear. The compleet “PRO-ELEMENTEN” header is gone.

Do you know why this is and how to fix this? Might be that the new version 2.0 is causing this?

Thanks,
Bjorn

Reply

Hey,
I’m trying to create basic widget with Elementor, following official docs. When following the developer documents to create a new widget, the following error is thrown
Fatal error: Class ‘Elementor\Widget_Base’ not found

Do you know how to fix that?
I’m stuck and don’t know where else to ask 🙂
Cheers.

Reply

Hi – Is there a way to create a widget (or plugin) that combines Elementor’s native datepicker and it’s native countdown timer so that a front end user could select a date on one page, and then have the countdown timer on another page (rather than setting the countdown timer’s paramenters from the back end).

Reply

I hope I’m not too late to the party. Using the current version from github I’m unable to get the widget to appear in the Elementor list. The code seems to expect the widget file in “plugins/elementor/my-widget.php” and it is definitely there.

However, “$template_file = locate_template( $widget_file );” returns nil, so the register_widget_type is never called.

What am I missing here ?

Reply

I dropped in the plugin files under wp-content/plugins/elementor-custom-element

The plugin shows up in the plugins list, I active it ok, but I don’t see any new icon in elementor widgets list..
???

Reply

Leave a Reply to Andreas Kviby Cancel reply

Your email address will not be published. Required fields are marked *