How to Build Custom Elementor Widget

Elementor is one of the most popular page builders for WordPress sites. Building a WordPress website using Elementor is easy and you can save a ton of time using this popular plugin. Elementor provides widgets for creating different elements on your pages. You just need to pick up a widget, configure it and it will display the rendered elements on the front end.

Though Elementor comes with predefined widgets, sometimes you can’t get widgets of your choice. You might want to build a specific functionality and that one is not covered in any widgets of Elementor. In such scenarios, you need a custom widget that fulfills your requirement. Fortunately, Elementor is easy to extend and you can achieve any type of functionality by creating an Elementor addon. Elementor add-ons are the same as regular WordPress plugins. They extend the basic functionality.

In this article, I show you how to build a custom Elementor widget which is nothing but an Elementor addon.

To see it practically, I am taking the example of Bootstrap Carousel. We will create a WordPress plugin that will provide the custom Elementor widget. This widget will give you the ability to add a Bootstrap carousel to your pages.

The purpose of this tutorial is to get you familiarized with Elementor widget development. I’ll cover the aspect required for the Bootstrap Carousel. Using the same approach, you can build your own Elementor widgets.

Creating an Elementor Addon

For getting started, you must have installed and activated the Elementor plugin. After this, create a folder artisansweb-elementor-addon inside the wp-content/plugins directory. Here to the folder name and in the next part of the code, I use the term artisansweb to keep unique references.

Inside the artisansweb-elementor-addon directory, create the artisansweb-elementor-addon.php file. This file will contain a code to initiate an instance of the Elementor addon.

Add the code below to the artisansweb-elementor-addon.php file.

<?php
/**
* Plugin Name: Elementor Widget - Bootstrap Carousel
* Plugin URI: https://artisansweb.net
* Description: Creates a Bootstrap Carousel.
* Author: Artisans Web
* Version: 1.0
* Author URI: https://artisansweb.net
*/

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

function artisansweb_elementor_addon() {

    // Load plugin file
    require_once( __DIR__ . '/includes/plugin.php' );

    // Run the plugin
    \Artisansweb_Elementor_Addon\Plugin::instance();

}
add_action( 'plugins_loaded', 'artisansweb_elementor_addon' );

This code initiates the addon by calling the instance() method of the Artisansweb_Elementor_Addon class. Notice in the method, we have included plugin.php from the includes directory. Create this includes folder and then the plugin.php file into it. Next, add the code below to the plugin.php file.

<?php
namespace Artisansweb_Elementor_Addon;

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

/**
 * Plugin class.
 *
 * The main class that initiates and runs the addon.
 *
 * @since 1.0.0
 */
final class Plugin {

    /**
     * Addon Version
     *
     * @since 1.0.0
     * @var string The addon version.
     */
    const VERSION = '1.0.0';

    /**
     * Minimum Elementor Version
     *
     * @since 1.0.0
     * @var string Minimum Elementor version required to run the addon.
     */
    const MINIMUM_ELEMENTOR_VERSION = '3.5.0';

    /**
     * Minimum PHP Version
     *
     * @since 1.0.0
     * @var string Minimum PHP version required to run the addon.
     */
    const MINIMUM_PHP_VERSION = '7.3';

    /**
     * Instance
     *
     * @since 1.0.0
     * @access private
     * @static
     * @var \Elementor_Test_Addon\Plugin The single instance of the class.
     */
    private static $_instance = null;

    /**
     * Instance
     *
     * Ensures only one instance of the class is loaded or can be loaded.
     *
     * @since 1.0.0
     * @access public
     * @static
     * @return \Elementor_Test_Addon\Plugin An instance of the class.
     */
    public static function instance() {

        if ( is_null( self::$_instance ) ) {
            self::$_instance = new self();
        }
        return self::$_instance;

    }

    /**
     * Constructor
     *
     * Perform some compatibility checks to make sure basic requirements are meet.
     * If all compatibility checks pass, initialize the functionality.
     *
     * @since 1.0.0
     * @access public
     */
    public function __construct() {

        if ( $this->is_compatible() ) {
            add_action( 'elementor/init', [ $this, 'init' ] );
        }

    }

    /**
     * Compatibility Checks
     *
     * Checks whether the site meets the addon requirement.
     *
     * @since 1.0.0
     * @access public
     */
    public function is_compatible() {

        // Check if Elementor installed and activated
        if ( ! did_action( 'elementor/loaded' ) ) {
            add_action( 'admin_notices', [ $this, 'admin_notice_missing_main_plugin' ] );
            return false;
        }

        // Check for required Elementor version
        if ( ! version_compare( ELEMENTOR_VERSION, self::MINIMUM_ELEMENTOR_VERSION, '>=' ) ) {
            add_action( 'admin_notices', [ $this, 'admin_notice_minimum_elementor_version' ] );
            return false;
        }

        // Check for required PHP version
        if ( version_compare( PHP_VERSION, self::MINIMUM_PHP_VERSION, '<' ) ) {
            add_action( 'admin_notices', [ $this, 'admin_notice_minimum_php_version' ] );
            return false;
        }

        return true;

    }

    /**
     * Admin notice
     *
     * Warning when the site doesn't have Elementor installed or activated.
     *
     * @since 1.0.0
     * @access public
     */
    public function admin_notice_missing_main_plugin() {

        if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

        $message = sprintf(
            /* translators: 1: Plugin name 2: Elementor */
            esc_html__( '"%1$s" requires "%2$s" to be installed and activated.', 'artisansweb-elementor-addon' ),
            '<strong>' . esc_html__( 'Artisansweb Elementor Addon', 'artisansweb-elementor-addon' ) . '</strong>',
            '<strong>' . esc_html__( 'Elementor', 'artisansweb-elementor-addon' ) . '</strong>'
        );

        printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

    }

    /**
     * Admin notice
     *
     * Warning when the site doesn't have a minimum required Elementor version.
     *
     * @since 1.0.0
     * @access public
     */
    public function admin_notice_minimum_elementor_version() {

        if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

        $message = sprintf(
            /* translators: 1: Plugin name 2: Elementor 3: Required Elementor version */
            esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'artisansweb-elementor-addon' ),
            '<strong>' . esc_html__( 'Artisansweb Elementor Addon', 'artisansweb-elementor-addon' ) . '</strong>',
            '<strong>' . esc_html__( 'Elementor', 'artisansweb-elementor-addon' ) . '</strong>',
             self::MINIMUM_ELEMENTOR_VERSION
        );

        printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

    }

    /**
     * Admin notice
     *
     * Warning when the site doesn't have a minimum required PHP version.
     *
     * @since 1.0.0
     * @access public
     */
    public function admin_notice_minimum_php_version() {

        if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

        $message = sprintf(
            /* translators: 1: Plugin name 2: PHP 3: Required PHP version */
            esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'artisansweb-elementor-addon' ),
            '<strong>' . esc_html__( 'Artisansweb Elementor Addon', 'artisansweb-elementor-addon' ) . '</strong>',
            '<strong>' . esc_html__( 'PHP', 'artisansweb-elementor-addon' ) . '</strong>',
             self::MINIMUM_PHP_VERSION
        );

        printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

    }

    /**
     * Initialize
     *
     * Load the addons functionality only after Elementor is initialized.
     *
     * Fired by `elementor/init` action hook.
     *
     * @since 1.0.0
     * @access public
     */
    public function init() {

        add_action( 'elementor/widgets/register', [ $this, 'register_widgets' ] );
        add_action( 'elementor/controls/register', [ $this, 'register_controls' ] );
    }

    /**
     * Register Widgets
     *
     * Load widgets files and register new Elementor widgets.
     *
     * Fired by `elementor/widgets/register` action hook.
     *
     * @param \Elementor\Widgets_Manager $widgets_manager Elementor widgets manager.
     */
    public function register_widgets( $widgets_manager ) {

        require_once( __DIR__ . '/widgets/artisansweb-testimonial-widget.php' );

        $widgets_manager->register( new \Artisansweb_Testmonial_Widget() );

    }

    /**
     * Register Controls
     *
     * Load controls files and register new Elementor controls.
     *
     * Fired by `elementor/controls/register` action hook.
     *
     * @param \Elementor\Controls_Manager $controls_manager Elementor controls manager.
     */
    public function register_controls( $controls_manager ) {
    }
}

The Plugin class written above handles a few tasks like

  • Check version compatibility with PHP and Elementor.
  • Register Elementor widget.
  • Register Elementor controls. We are not going to create these controls but keep this method declared. You can explore it later.

In the register_widgets() method, we have written the below statement.

require_once( __DIR__ . '/widgets/artisansweb-testimonial-widget.php' );

That means you need to create a widgets folder inside the includes directory. And create the artisansweb-testimonial-widget.php file in the widgets folder. In this file, we will write the code which gives settings for the widget and displays the final output on the front end.

Elementor Widget Structure

Elementor provides a structure to create the widget. It’s just like the default WordPress widgets where you need to define a few methods.

In Elementor, your widget class must extend the \Elementor\Widget_Base class and fill in all required methods.

The basic structure of the Elementor widget is as follows.

artisansweb-testimonial-widget.php

<?php
class Artisansweb_Testmonial_Widget extends \Elementor\Widget_Base {

    public function get_name() {}

    public function get_title() {}

    public function get_icon() {}

    public function get_custom_help_url() {}

    public function get_categories() {}

    public function get_keywords() {}

    public function get_script_depends() {}

    public function get_style_depends() {}

    protected function register_controls() {}

    protected function render() {}

    protected function content_template() {}

}

Here, we’ll not define all methods. The methods like get_custom_help_url(), get_keywords(), register_controls(), content_template() are not required to be defined for this tutorial.

Define Elementor Widget Methods

The few methods I’ll define are as follows.

  • get_name() : Pass the widget name as it will be used in the code.
  • get_title() : Widget label.
  • get_icon() : It lets you set the widget icon. I’ll use ‘fa fa-sliders’ of FontAwesome which is bundled by default.
  • get_categories() : Set the widget category. I’ll set it to ‘General’.
  • get_script_depends() : Enqueue required JS files.
  • get_style_depends() : Enqueue required CSS files.
  • register_controls() : Here we define controls so the user can select the data. In our case, it will be the image and title.
  • render() : Display the output.

Having said that, the code for artisansweb-testimonial-widget.php will be as follows.

<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

class Artisansweb_Testmonial_Widget extends \Elementor\Widget_Base {
 
    public function get_name() {
        return 'artisansweb-carousel';
    }
 
    public function get_title() {
        return __( 'Artisansweb Carousel', 'artisansweb-elementor-add-on' );
    }
 
    public function get_icon() {
        return 'fa fa-sliders';
    }
 
    public function get_categories() {
        return [ 'general' ];
    }

    public function get_script_depends() {
        wp_register_script("bootstrap-js", "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js", array(), false, true);
        
        return [
            'bootstrap-js'
        ];
    }

    public function get_style_depends() {
        wp_register_style( "bootstrap-css", "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css", array(), false, "all" );
        return [
            'bootstrap-css'
        ];
    }
 
    protected function register_controls() {
 
        $this->start_controls_section(
            'content_section',
            [
                'label' => esc_html__( 'Content', 'artisansweb-elementor-add-on' ),
                'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
            ]
        );
 
        $repeater = new \Elementor\Repeater();
 
        $repeater->add_control(
            'list_title', [
                'label' => esc_html__( 'Title', 'artisansweb-elementor-add-on' ),
                'type' => \Elementor\Controls_Manager::TEXT,
                'default' => esc_html__( 'List Title' , 'artisansweb-elementor-add-on' ),
                'label_block' => true,
            ]
        );
 
        $repeater->add_control(
            'list_image',
            [
                'label' => esc_html__( 'Choose Image', 'artisansweb-elementor-add-on' ),
                'type' => \Elementor\Controls_Manager::MEDIA,
                'default' => [
                    'url' => \Elementor\Utils::get_placeholder_image_src(),
                ],
            ]
        );
 
        $this->add_control(
            'list',
            [
                'label' => esc_html__( 'Repeater List', 'artisansweb-elementor-add-on' ),
                'type' => \Elementor\Controls_Manager::REPEATER,
                'fields' => $repeater->get_controls(),
                'default' => [
                    [
                        'list_title' => esc_html__( 'Title #1', 'artisansweb-elementor-add-on' ),
                        'list_image' => esc_html__( 'Item image.', 'artisansweb-elementor-add-on' ),
                    ],
                    [
                        'list_title' => esc_html__( 'Title #2', 'artisansweb-elementor-add-on' ),
                        'list_image' => esc_html__( 'Item image.', 'artisansweb-elementor-add-on' ),
                    ],
                ],
                'title_field' => '{{{ list_title }}}',
            ]
        );
 
        $this->end_controls_section();
 
    }
     
    protected function render() {
        // generate the final HTML on the frontend using PHP
        $settings = $this->get_settings_for_display();
 
        if ( $settings['list'] ) {
            ?>
            <div id="carouselExampleCaptions" class="carousel slide" data-bs-ride="carousel">
                <div class="carousel-indicators">
                    <?php for($i=0; $i<count($settings['list']); $i++) { ?>
                        <button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="<?php echo $i; ?>" <?php if($i==0){ ?> class="active" aria-current="true" <?php } ?>></button>
                    <?php } ?>
                </div>
                <div class="carousel-inner">
                    <?php $i = 0; ?>
                    <?php foreach (  $settings['list'] as $item ) { ?>
                        <div class="carousel-item <?php echo ($i==0) ? 'active':''; ?>">
                            <img class="d-block w-100" src="<?php echo $item['list_image']['url']; ?>" alt="<?php echo $item['list_title']; ?>" />
                            <div class="carousel-caption d-none d-md-block">
                                <h3><?php echo $item['list_title']; ?></h3>
                            </div>
                        </div>
                        <?php $i++; ?>
                    <?php } ?>
                </div>
                <button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide="prev">
                    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                    <span class="visually-hidden">Previous</span>
                </a>
                <button class="carousel-control-next" type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide="next">
                    <span class="carousel-control-next-icon" aria-hidden="true"></span>
                    <span class="visually-hidden">Next</span>
                </a>
            </div>
            <?php
        }
    }
}

We are done with building the Elementor widget. Now go to the Plugins page, you should see the plugin ‘Elementor Widget – Bootstrap Carousel’. Activate this plugin.

Plugin

Upon plugin activation, try to edit your page with Elementor. Search for ‘Artisansweb Carousel’, and add this widget to the page. You will see the options to add captions and images as shown in the screenshot below.

Elementor Widget

I have used an Elementor repeater control which allows you to build repeatable blocks of fields. As we are creating a carousel, it will have multiple fields. That’s why I gave the option for adding an image and caption using a repeater control.

Along with repeater control, I also used Text and Media control. Through these controls, one can add text and images respectively. Click here to read more about Elementor controls.

Add your images and captions and you should see the Bootstrap carousel appear on your page.

Carousel

If you look at the render() method, you’ll see we used the HTML of Bootstrap Carousel and made it dynamic.

It’s all about widget development in Elementor. In a similar way, you can customize the code to use different Elementor controls and generate custom widgets as per your requirements. Check out the documentation for more details on the custom Elementor widget.

Related Articles

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

3 thoughts on “How to Build Custom Elementor Widget

  1. I faced error in artisansweb-testimonial-widget.php file at render() which is Undefined variable: settings
    please help

Leave a Reply

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