Azwar's Blog

Elementor Development: Creating Custom Widgets and Extending Functionality

December 10, 2024 (7mo ago)WordPress

Hi everyone! I'm Azwar, a WordPress and Elementor developer. In this comprehensive guide, I'll walk you through the process of creating custom Elementor widgets and extending the page builder's functionality. Elementor is one of the most popular WordPress page builders, and learning to develop for it opens up incredible opportunities for customization.

Elementor Development: Creating Custom Widgets
Elementor Development: Creating Custom Widgets

Elementor provides a powerful framework for creating custom widgets that integrate seamlessly with the page builder interface. Whether you're building widgets for clients or creating your own solutions, understanding Elementor development allows you to create unique, functional components.

Understanding Elementor Widget Structure

Every Elementor widget follows a specific structure and extends the base widget class:

<?php /** * Plugin Name: Custom Elementor Widgets * Description: Custom widgets for Elementor page builder * Version: 1.0.0 * Author: Azwar * Text Domain: custom-elementor-widgets */ if (!defined('ABSPATH')) { exit; } final class Custom_Elementor_Widgets { private static $_instance = null; public static function instance() { if (is_null(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } public function __construct() { add_action('plugins_loaded', [$this, 'init']); } public function init() { // Check if Elementor is installed and activated if (!did_action('elementor/loaded')) { add_action('admin_notices', [$this, 'admin_notice_missing_elementor']); return; } // Add Widget categories add_action('elementor/elements/categories_registered', [$this, 'add_widget_categories']); // Register widgets add_action('elementor/widgets/register', [$this, 'init_widgets']); // Register editor scripts add_action('elementor/editor/before_enqueue_scripts', [$this, 'editor_scripts']); } public function admin_notice_missing_elementor() { if (isset($_GET['activate'])) unset($_GET['activate']); $message = sprintf( esc_html__('"%1$s" requires "%2$s" to be installed and activated.', 'custom-elementor-widgets'), '<strong>' . esc_html__('Custom Elementor Widgets', 'custom-elementor-widgets') . '</strong>', '<strong>' . esc_html__('Elementor', 'custom-elementor-widgets') . '</strong>' ); printf('<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message); } public function add_widget_categories($elements_manager) { $elements_manager->add_category( 'custom-widgets', [ 'title' => esc_html__('Custom Widgets', 'custom-elementor-widgets'), 'icon' => 'fa fa-plug', ] ); } public function init_widgets($widgets_manager) { require_once(__DIR__ . '/widgets/custom-widget.php'); $widgets_manager->register(new \Custom_Widget()); } public function editor_scripts() { wp_enqueue_script( 'custom-elementor-widgets-editor', plugins_url('assets/js/editor.js', __FILE__), ['elementor-editor'], '1.0.0', true ); } } Custom_Elementor_Widgets::instance();

Step 1: Creating a Basic Custom Widget

Create a custom widget that extends Elementor's base widget class:

<?php // widgets/custom-widget.php class Custom_Widget extends \Elementor\Widget_Base { public function get_name() { return 'custom_widget'; } public function get_title() { return esc_html__('Custom Widget', 'custom-elementor-widgets'); } public function get_icon() { return 'eicon-code'; } public function get_categories() { return ['custom-widgets']; } public function get_keywords() { return ['custom', 'widget', 'elementor']; } protected function register_controls() { // Content Section $this->start_controls_section( 'content_section', [ 'label' => esc_html__('Content', 'custom-elementor-widgets'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'title', [ 'label' => esc_html__('Title', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => esc_html__('Custom Widget Title', 'custom-elementor-widgets'), 'placeholder' => esc_html__('Enter your title', 'custom-elementor-widgets'), ] ); $this->add_control( 'description', [ 'label' => esc_html__('Description', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => esc_html__('This is a custom widget description.', 'custom-elementor-widgets'), 'placeholder' => esc_html__('Enter your description', 'custom-elementor-widgets'), ] ); $this->add_control( 'link', [ 'label' => esc_html__('Link', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::URL, 'placeholder' => esc_html__('https://your-link.com', 'custom-elementor-widgets'), 'default' => [ 'url' => '', 'is_external' => true, 'nofollow' => true, 'custom_attributes' => '', ], ] ); $this->end_controls_section(); // Style Section $this->start_controls_section( 'style_section', [ 'label' => esc_html__('Style', 'custom-elementor-widgets'), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'title_color', [ 'label' => esc_html__('Title Color', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .custom-widget-title' => 'color: {{VALUE}};', ], ] ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), [ 'name' => 'title_typography', 'selector' => '{{WRAPPER}} .custom-widget-title', ] ); $this->end_controls_section(); } protected function render() { $settings = $this->get_settings_for_display(); ?> <div class="custom-widget"> <?php if (!empty($settings['title'])) : ?> <h3 class="custom-widget-title"> <?php if (!empty($settings['link']['url'])) : ?> <a href="<?php echo esc_url($settings['link']['url']); ?>" <?php echo $settings['link']['is_external'] ? 'target="_blank"' : ''; ?> <?php echo $settings['link']['nofollow'] ? 'rel="nofollow"' : ''; ?>> <?php echo esc_html($settings['title']); ?> </a> <?php else : ?> <?php echo esc_html($settings['title']); ?> <?php endif; ?> </h3> <?php endif; ?> <?php if (!empty($settings['description'])) : ?> <p class="custom-widget-description"> <?php echo esc_html($settings['description']); ?> </p> <?php endif; ?> </div> <?php } protected function content_template() { ?> <div class="custom-widget"> <# if (settings.title) { #> <h3 class="custom-widget-title"> <# if (settings.link.url) { #> <a href="{{ settings.link.url }}" <# if (settings.link.is_external) { #>target="_blank"<# } #> <# if (settings.link.nofollow) { #>rel="nofollow"<# } #>> {{{ settings.title }}} </a> <# } else { #> {{{ settings.title }}} <# } #> </h3> <# } #> <# if (settings.description) { #> <p class="custom-widget-description"> {{{ settings.description }}} </p> <# } #> </div> <?php } }

Step 2: Creating Advanced Widget Controls

Add more sophisticated controls to your widget:

protected function register_controls() { // Content Section $this->start_controls_section( 'content_section', [ 'label' => esc_html__('Content', 'custom-elementor-widgets'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); // Image Control $this->add_control( 'image', [ 'label' => esc_html__('Choose Image', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::MEDIA, 'default' => [ 'url' => \Elementor\Utils::get_placeholder_image_src(), ], ] ); // Gallery Control $this->add_control( 'gallery', [ 'label' => esc_html__('Add Images', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::GALLERY, 'default' => [], ] ); // Select Control $this->add_control( 'layout', [ 'label' => esc_html__('Layout', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::SELECT, 'default' => 'grid', 'options' => [ 'grid' => esc_html__('Grid', 'custom-elementor-widgets'), 'list' => esc_html__('List', 'custom-elementor-widgets'), 'carousel' => esc_html__('Carousel', 'custom-elementor-widgets'), ], ] ); // Number Control $this->add_control( 'columns', [ 'label' => esc_html__('Columns', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::NUMBER, 'min' => 1, 'max' => 6, 'default' => 3, 'condition' => [ 'layout' => 'grid', ], ] ); // Switcher Control $this->add_control( 'show_title', [ 'label' => esc_html__('Show Title', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::SWITCHER, 'label_on' => esc_html__('Show', 'custom-elementor-widgets'), 'label_off' => esc_html__('Hide', 'custom-elementor-widgets'), 'return_value' => 'yes', 'default' => 'yes', ] ); // Repeater Control $repeater = new \Elementor\Repeater(); $repeater->add_control( 'item_title', [ 'label' => esc_html__('Item Title', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => esc_html__('Item Title', 'custom-elementor-widgets'), ] ); $repeater->add_control( 'item_description', [ 'label' => esc_html__('Item Description', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => esc_html__('Item Description', 'custom-elementor-widgets'), ] ); $this->add_control( 'items', [ 'label' => esc_html__('Items', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::REPEATER, 'fields' => $repeater->get_controls(), 'default' => [ [ 'item_title' => esc_html__('Item #1', 'custom-elementor-widgets'), 'item_description' => esc_html__('Item #1 Description', 'custom-elementor-widgets'), ], [ 'item_title' => esc_html__('Item #2', 'custom-elementor-widgets'), 'item_description' => esc_html__('Item #2 Description', 'custom-elementor-widgets'), ], ], 'title_field' => '{{{ item_title }}}', ] ); $this->end_controls_section(); }

Step 3: Creating a Custom Widget with AJAX

Create a widget that uses AJAX to load dynamic content:

class Ajax_Widget extends \Elementor\Widget_Base { public function get_name() { return 'ajax_widget'; } public function get_title() { return esc_html__('AJAX Widget', 'custom-elementor-widgets'); } public function get_icon() { return 'eicon-sync'; } public function get_categories() { return ['custom-widgets']; } protected function register_controls() { $this->start_controls_section( 'content_section', [ 'label' => esc_html__('Content', 'custom-elementor-widgets'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'button_text', [ 'label' => esc_html__('Button Text', 'custom-elementor-widgets'), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => esc_html__('Load Content', 'custom-elementor-widgets'), ] ); $this->end_controls_section(); } protected function render() { $settings = $this->get_settings_for_display(); ?> <div class="ajax-widget" data-widget-id="<?php echo esc_attr($this->get_id()); ?>"> <button class="ajax-load-button"> <?php echo esc_html($settings['button_text']); ?> </button> <div class="ajax-content"></div> </div> <?php } public function __construct($data = [], $args = null) { parent::__construct($data, $args); wp_register_script( 'ajax-widget-script', plugins_url('assets/js/ajax-widget.js', __FILE__), ['jquery'], '1.0.0', true ); wp_localize_script('ajax-widget-script', 'ajax_widget_ajax', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('ajax_widget_nonce'), ]); } public function get_script_depends() { return ['ajax-widget-script']; } }

Step 4: Creating Custom CSS for Your Widget

Add custom CSS to style your widget:

public function get_style_depends() { wp_register_style( 'custom-widget-style', plugins_url('assets/css/custom-widget.css', __FILE__), [], '1.0.0' ); return ['custom-widget-style']; }

Create the CSS file:

/* assets/css/custom-widget.css */ .custom-widget { padding: 20px; border: 1px solid #ddd; border-radius: 5px; background: #fff; } .custom-widget-title { margin: 0 0 15px 0; font-size: 24px; font-weight: bold; } .custom-widget-title a { color: inherit; text-decoration: none; } .custom-widget-title a:hover { color: #007cba; } .custom-widget-description { margin: 0; line-height: 1.6; color: #666; } .ajax-widget { text-align: center; } .ajax-load-button { background: #007cba; color: white; border: none; padding: 12px 24px; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background 0.3s ease; } .ajax-load-button:hover { background: #005a87; } .ajax-content { margin-top: 20px; padding: 20px; background: #f9f9f9; border-radius: 4px; min-height: 100px; }

Step 5: Creating JavaScript for AJAX Functionality

Create the JavaScript file for AJAX functionality:

// assets/js/ajax-widget.js jQuery(document).ready(function($) { $('.ajax-load-button').on('click', function() { var button = $(this); var widget = button.closest('.ajax-widget'); var content = widget.find('.ajax-content'); var widgetId = widget.data('widget-id'); button.prop('disabled', true).text('Loading...'); $.ajax({ url: ajax_widget_ajax.ajax_url, type: 'POST', data: { action: 'load_ajax_content', nonce: ajax_widget_ajax.nonce, widget_id: widgetId }, success: function(response) { if (response.success) { content.html(response.data.content); } else { content.html('<p>Error loading content.</p>'); } }, error: function() { content.html('<p>Error loading content.</p>'); }, complete: function() { button.prop('disabled', false).text('Load Content'); } }); }); });

Step 6: Handling AJAX Requests

Add the AJAX handler to your main plugin file:

// Add this to your main plugin class public function __construct() { // ... existing code ... add_action('wp_ajax_load_ajax_content', [$this, 'load_ajax_content']); add_action('wp_ajax_nopriv_load_ajax_content', [$this, 'load_ajax_content']); } public function load_ajax_content() { check_ajax_referer('ajax_widget_nonce', 'nonce'); $widget_id = sanitize_text_field($_POST['widget_id']); // Generate some sample content $content = '<h3>Dynamic Content Loaded!</h3>'; $content .= '<p>This content was loaded via AJAX for widget ID: ' . esc_html($widget_id) . '</p>'; $content .= '<p>Current time: ' . current_time('Y-m-d H:i:s') . '</p>'; wp_send_json_success([ 'content' => $content ]); }

Best Practices for Elementor Widget Development

  1. Follow Elementor Conventions: Use Elementor's naming conventions and structure
  2. Optimize Performance: Minimize JavaScript and CSS files
  3. Security First: Always sanitize and validate data
  4. Responsive Design: Ensure your widgets work on all devices
  5. Documentation: Provide clear documentation for your widgets
  6. Testing: Test your widgets thoroughly before release
  7. Compatibility: Ensure compatibility with different themes and plugins

Conclusion

Elementor widget development is a powerful skill that allows you to create custom functionality for the popular page builder. By following this guide and practicing regularly, you'll develop the skills needed to create professional, user-friendly Elementor widgets.

Remember to always prioritize user experience, follow Elementor best practices, and create widgets that provide real value to users.

Additional Resources

Comments