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.

15 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

Leave a Reply

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