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 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).



 * Plugin Name: Elementor Custom Elements
 * Description: Custom element added to Elementor
 * Plugin URI:
 * Version: 0.0.1
 * Author: dtbaker
 * Author URI:
 * 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;




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:

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
      return 'post-list';

   protected function _register_controls() {

            'label' => __( 'Blog Posts', 'elementor-custom-element' ),
            'type' => Controls_Manager::SECTION,

            'label' => __( 'Text', 'elementor-custom-element' ),
            'type' => Controls_Manager::TEXT,
            'default' => '',
            'title' => __( 'Enter some text', 'elementor-custom-element' ),
            'section' => 'section_blog_posts',

            '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>
         $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> ';



   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:


Code is on Github

The above code is over on Github, check it out here:


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.

Helpful Hacker Program Badge

Envato Product Security

Security and privacy at Envato starts with our values. At Envato, we understand security and privacy is important because we are in it for the community. This means we’re committed to working with our community, including through our security program to recognise helpful hackers that work with Envato.

We are proud members of the Envato Helpful hacker program.

Envato Power Elite Author

The Envato Elite Author program rewards the best selling digital creatives on the Envato Market. We recently reached 1 Million Dollars in sales.  Unlocking this achievement put us into the Envato Hall of Fame, and lets our buyers know they are dealing with seasoned professionals on the marketplace.

reached goal
The moment we cracked 1 million dollars in sales - Cue Fireworks!

We feel so proud, its been a long time coming with many late nights, rejections, approvals, celebrations and everything in between. Reaching 1 million dollars in sales has given us a welcome boost of inspiration to create more, streamline our processes and continue to be apart of the Envato Marketplaces.

Envato Power Elite Wall of Fame

We Made it!

The Wall of Fame is for creative teams that have sold more than $1 million dollars worth of items on the Envato Marketplaces.   The Envato Elite Author program rewards the best selling digital creatives on the Envato Market.

We recently reached 1 Million Dollars in sales.  Unlocking this achievement put us into the Envato Hall of Fame, and lets our buyers know they are dealing with seasoned professionals on the marketplace.

How to reset the root password on a Raspberry Pi

Put the Raspberry Pi SD card into a linux computer.

(If you do not have a linux computer you will need to install software that can read linux file systems)

Open the file /etc/passwd located on the SD card (you may need to be a super user to access this file on the SD card)

You should see the “pi” user, it will look something like this:


Replace this complete line with the following:


This is the encrypted value for “password”. So now the password for the “pi” user is simply “password”.

Put the SD card back into the Pi, boot it up, login using pi/password, then change your password again.



Featured in Envato’s 10 years Article

Digital nomads sell their home for a family working holiday adventure

10 years of empowering a global creative economy

In its tenth year, Envato’s global community of over seven million creatives reached new heights.

Collectively, sellers across Envato have earned over $400 million. Those sellers are breaking geographic and educational barriers. All over the world, Envato’s community members are carving out the lives they want to lead by following their own rules. They are working when and where they want, on the projects that they’re passionate about. They are providing top quality products and services to a savvy pool of buyers. Through the Envato marketplace, superior themes, audio tracks, video files and more are utilized for a vast array of high calibre projects, including major motion pictures, podcasts, websites, apps and much more.

Married couple Dave and Hayley, of DT Baker, set up their own small business after meeting at a large web firm. After catching the travel bug, they were at a crossroads. They could hire staff, get an office and continue to grow the business, or try something else that would allow them to work from anywhere with increased flexibility. They chose the latter and started selling web design and PHP programming on ThemeForest in 2009.

“When that little $0.00 in the top right corner ticked over to $2.00 my world changed right before my eyes. This was our pivot point. We could make money while we slept,” Dave says.

We had found the perfect way to work and travel.

Dave and Hayley threw all their energy into creating themes, PHP scripts and WordPress plugins to sell on Envato Market. The fruits of their labour were evident when they took their first working holiday. “We were answering emails and creating new items one day, then sightseeing the next,” says Dave. “We had found the perfect way to work and travel.”


Last year, Dave and Hayley took the working holiday to new heights by selling the family home and travelling along the East Coast of Australia for nine months with their two young children.

The daunting prospect of packing up and moving was liberating for the family. “The biggest thing I learned from the trip is how few possessions you need to live happily,” says Dave.

“I really didn’t know how the four of us would go with only a handful of things in the van. It is really refreshing to not be surrounded by so many material goods, and we’re all much happier for it.”

Dave says he and Hayley still throw around the idea of going back into an office job once their children hit school age in a few years time. But for now, they are relishing the opportunity to spend quality time with the family while still been able to work from anywhere with only a laptop. They hope to take another extended working holiday when their children get a bit older, and perhaps visit some of the larger tech conferences overseas while they’re at it.

Debugging slow WordPress Theme demos with NewRelic

A few of our WordPress theme demos started to run slowly. Time to fire up newrelic to find out the problem.

It quickly stood out that WordPress SQL queries are chewing up most of the time:


What did I find in these demo database tables? 18000 WooCommerce “wc_session” entries.


All of these wc_session entries had “autoload” set to “no”. When WordPress starts it does a SQL query a bit like this:

SELECT * FROM wp_demo_options WHERE autoload = "yes"

But – there is no MySQL index on “autoload”! (Google for reasons).


So this SQL query was very expensive, since 99.99% of the option entries (wc_session) in this database table have autoload = “no”.

The quick fix for my live demos was to delete all %wc_session% entries and add an autoload index so it doesn’t slow down again in the future.