---
title: "Elementor Widget Part 2: Understanding Elementor Widget Architecture"
description: "# Introduction This part explains the Elementor widget base class, required methods and lifecycle. We’ll use these concepts to build the Collection…"
url: "https://sajdoko.com/blog/elementor-widget-part-2-understanding-elementor-widget-architecture/"
language: "en"
content_signal: "ai-train=no, search=yes, ai-input=yes"
---
# Elementor Widget Part 2: Understanding Elementor Widget Architecture

 10 November 2025·9 minutes read·[Development](https://sajdoko.com/blog/category/development/)     

## [#](#introduction) Introduction

 

This part explains the Elementor widget base class, required methods and lifecycle. We’ll use these concepts to build the Collection Products widget in the next part.

 

## [#](#learning-objectives) Learning Objectives

 

By the end of this tutorial, you will understand:

 

- - The structure of an Elementor widget class - Required methods and their purposes - How widgets are registered with Elementor

 

- The widget lifecycle from registration to rendering
- Widget controls and how they create the settings panel
- How widgets render content on the frontend
- Best practices for widget development

 

## [#](#the-elementor-widget-base-class) The Elementor Widget Base Class

 

All Elementor widgets extend the `\Elementor\Widget_Base` abstract class. This base class provides the foundation and structure that Elementor expects from every widget.

 

### [#](#key-concept%3A-object-oriented-programming) Key Concept: Object-Oriented Programming

 

Elementor uses Object-Oriented Programming (OOP) principles. Your widget is a PHP class that extends (inherits from) the base widget class.

 

```
class My_Custom_Widget extends \Elementor\Widget_Base {
      // Your widget code here
    }
```

 

## [#](#required-methods) Required Methods

 

Every Elementor widget **must** implement certain methods. Let’s examine each one:

 

### [#](#1.-get_name()) 1. get_name()

 

**Purpose:** Returns a unique identifier for your widget.

 

```
public function get_name() {
    return 'collection_products';
  }
```

 

**Important Points:**

 

- Must be unique across all widgets
- Use lowercase letters, numbers, and underscores only
- No spaces or special characters
- This name is used internally by Elementor

 

### [#](#2.-get_title()) 2. get_title()

 

**Purpose:** Returns the human-readable title shown in the widget panel.

 

```
public function get_title() {
    return __( 'Collection Products', 'hello-biz-child' );
  }
```

 

**Important Points:**

 

- This is what users see in the Elementor editor
- Use `__()` for translation support
- Keep it clear and descriptive

 

### [#](#3.-get_icon()) 3. get_icon()

 

**Purpose:** Returns the icon class that appears next to the widget name.

 

```
public function get_icon() {
    return 'eicon-products';
  }
```

 

**Important Points:**

 

- Uses Elementor’s icon library
- Format: `eicon-{icon-name}`
- Browse available icons in Elementor’s icon library
- Common icons: `eicon-posts-grid`, `eicon-products`, `eicon-gallery-grid`

 

### [#](#4.-get_categories()) 4. get_categories()

 

**Purpose:** Defines which category panel the widget appears in.

 

```
public function get_categories() {
    return [ 'woocommerce-elements' ];
  }
```

 

**Available Categories:**

 

- `basic` – Basic widgets
- `general` – General elements
- `woocommerce-elements` – WooCommerce widgets (we’ll use this)
- `theme-elements` – Theme-specific widgets
- You can also create custom categories

 

### [#](#5.-register_controls()-(optional-but-essential)) 5. register_controls() (Optional but Essential)

 

**Purpose:** Defines the widget’s settings panel in the Elementor editor.

 

```
protected function register_controls() {
    // Define control sections and controls here
  }
```

 

**Important Points:**

 

- This method is `protected` (not `public`)
- Controls are settings users can modify in the Elementor panel
- Controls are organized into sections
- This is where you define all user-configurable options

 

### [#](#6.-render()) 6. render()

 

**Purpose:** Outputs the HTML for the frontend display.

 

```
protected function render() {
    $settings = $this->get_settings_for_display();

    // Output your HTML here
    echo '
```

 ‘; echo ‘Hello from my widget!’; echo ‘ 

```

```

 

‘; }

 

```

```

 

**Important Points:**

 

- This method is `protected`
- Use `$this->get_settings_for_display()` to access user settings
- Output sanitized HTML
- This code runs on the frontend when the page loads

 

## [#](#widget-lifecycle) Widget Lifecycle

 

Understanding when methods are called helps you structure your code effectively:

 

1. **Widget Registration** - WordPress loads - Elementor initializes - Your widget class is registered via `elementor/widgets/register` hook
2. **Editor Load** - User opens Elementor editor - `get_name()`, `get_title()`, `get_icon()`, `get_categories()` are called - Widget appears in the panel
3. **Widget Inserted** - User drags widget onto page - `register_controls()` is called - Control panel appears in the editor
4. **Settings Changed** - User modifies controls - Widget preview updates in editor - `render()` is called for preview
5. **Page Saved** - Settings are saved to the database - Widget configuration is stored
6. **Frontend Display** - Visitor loads the page - `render()` is called - HTML is output to the page

 

## [#](#understanding-controls) Understanding Controls

 

Controls are the building blocks of your widget’s settings panel. Let’s break down how they work:

 

### [#](#control-sections) Control Sections

 

Controls are organized into sections. A section is a collapsible panel in the editor.

 

```
protected function register_controls() {
    // Start a new section
    $this->start_controls_section(
      'section_content', // Section ID (must be unique within widget)
      [
        'label' => __( 'Content', 'hello-biz-child' ), // Section title
      ]
    );

    // Add controls here

    // End the section
    $this->end_controls_section();
  }
```

 

### [#](#adding-controls) Adding Controls

 

Between `start_controls_section()` and `end_controls_section()`, you add individual controls:

 

```
$this->add_control(
    'products_per_page', // Control ID (must be unique within widget)
    [
      'label' => __( 'Products Per Page', 'hello-biz-child' ),
      'type' => \Elementor\Controls_Manager::NUMBER,
      'default' => 9,
      'min' => 3,
    ]
  );
```

 

### [#](#common-control-types) Common Control Types

 

**NUMBER** – Numeric input

 

```
'type' => \Elementor\Controls_Manager::NUMBER,
'default' => 9,
'min' => 1,
'max' => 100,
'step' => 3,
```

 

**SELECT** – Dropdown menu

 

```
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'date',
'options' => [
    'date' => __( 'Date', 'hello-biz-child' ),
    'title' => __( 'Title', 'hello-biz-child' ),
    'price' => __( 'Price', 'hello-biz-child' ),
],
```

 

**SELECT2** – Multi-select dropdown

 

```
'type' => \Elementor\Controls_Manager::SELECT2,
'multiple' => true,
'options' => $this->get_categories(),
'label_block' => true,
```

 

**SWITCHER** – Toggle switch (yes/no)

 

```
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => __( 'Yes', 'hello-biz-child' ),
'label_off' => __( 'No', 'hello-biz-child' ),
'return_value' => 'yes',
'default' => 'yes',
```

 

**SLIDER** – Range slider

 

```
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => [ 'px' ],
'range' => [
    'px' => [
        'min' => 0,
        'max' => 50,
        'step' => 1,
    ],
],
'default' => [
    'unit' => 'px',
    'size' => 10,
],
```

 

**TEXT** – Text input

 

```
'type' => \Elementor\Controls_Manager::TEXT,
'default' => __( 'Default text', 'hello-biz-child' ),
'placeholder' => __( 'Enter text', 'hello-biz-child' ),
```

 

## [#](#style-controls) Style Controls

 

For styling options, use the `TAB_STYLE` tab:

 

```
$this->start_controls_section(
    'section_style',
    [
      'label' => __( 'Style', 'hello-biz-child' ),
      'tab' => \Elementor\Controls_Manager::TAB_STYLE, // Style tab
    ]
  );
```

 

### [#](#using-selectors) Using Selectors

 

Selectors allow controls to directly affect CSS:

 

```
$this->add_control(
  'gap',
  [
    'label' => __( 'Gap', 'hello-biz-child' ),
    'type' => \Elementor\Controls_Manager::SLIDER,
    'selectors' => [
      '{{WRAPPER}} .my-grid' => 'gap: {{SIZE}}{{UNIT}};',
    ],
  ]
);
```

 

**Key Points:**

 

- `{{WRAPPER}}` is replaced with the widget’s unique wrapper selector
- `{{SIZE}}` is replaced with the slider value
- `{{UNIT}}` is replaced with the selected unit (px, %, em, etc.)

 

## [#](#accessing-settings-in-render()) Accessing Settings in render()

 

To use the settings in your widget:

 

```
protected function render() {
      // Get settings
      $settings = $this->get_settings_for_display();
      
      // Access individual settings
      $products_per_page = $settings['products_per_page'];
      $orderby = $settings['orderby'];
      $show_filters = $settings['show_filters'];
      
      // Use in your code
      if ( $show_filters === 'yes' ) {
          // Display filters
      }
  }
```

 

## [#](#widget-registration) Widget Registration

 

To make your widget available in Elementor, you must register it:

 

```
function register_my_widget( $widgets_manager ) {
    require_once( __DIR__ . '/my-widget.php' );
    $widgets_manager->register( new \My_Custom_Widget() );
}
add_action( 'elementor/widgets/register', 'register_my_widget' );
```

 

**Important Points:**

 

- Hook into `elementor/widgets/register`
- Require/include your widget file
- Instantiate and register your widget class

 

## [#](#best-practices) Best Practices

 

### [#](#1.-namespace-your-widget) 1. Namespace Your Widget

 

Avoid naming conflicts by using a unique class name:

 

```
// Good
class Collection_Products_Widget extends \Elementor\Widget_Base {

// Better (with namespace)
namespace MyTheme\Widgets;
class Collection_Products extends \Elementor\Widget_Base {
```

 

### [#](#2.-translation-ready) 2. Translation Ready

 

Always use translation functions:

 

```
// Good
'label' => __( 'Products Per Page', 'hello-biz-child' ),

// Bad
'label' => 'Products Per Page',
```

 

### [#](#3.-sanitize-output) 3. Sanitize Output

 

Always escape output in `render()`:

 

```
// Good
echo '<div class="' . esc_attr( $class ) . '">';
echo esc_html( $title );

// Bad
echo '<div class="' . $class . '">';
echo $title;
```

 

### [#](#4.-organize-controls) 4. Organize Controls

 

Group related controls into sections:

 

```
// Query settings section
$this->start_controls_section( 'section_query', [...] );

// Display settings section
$this->start_controls_section( 'section_display', [...] );

// Style settings section  
$this->start_controls_section( 'section_style', [...] );
```

 

### [#](#5.-provide-defaults) 5. Provide Defaults

 

Always provide sensible default values:

 

```
'default' => 9,  // Good default for products per page
'default' => 'date',  // Reasonable sort option
```

 

## [#](#example%3A-simple-widget-structure) Example: Simple Widget Structure

 

Here’s a complete example of a minimal widget:

 

```
<?php
class Simple_Widget extends \Elementor\Widget_Base {

    // Required: Unique widget name
    public function get_name() {
        return 'simple_widget';
    }

    // Required: Widget title in panel
    public function get_title() {
        return __( 'Simple Widget', 'hello-biz-child' );
    }

    // Required: Widget icon
    public function get_icon() {
        return 'eicon-posts-grid';
    }

    // Required: Widget category
    public function get_categories() {
        return [ 'general' ];
    }

    // Optional: Widget keywords for search
    public function get_keywords() {
        return [ 'simple', 'example' ];
    }

    // Define settings controls
    protected function register_controls() {
        
        // Content Section
        $this->start_controls_section(
            'content_section',
            [
                'label' => __( 'Content', 'hello-biz-child' ),
            ]
        );

        $this->add_control(
            'title',
            [
                'label' => __( 'Title', 'hello-biz-child' ),
                'type' => \Elementor\Controls_Manager::TEXT,
                'default' => __( 'Hello World', 'hello-biz-child' ),
            ]
        );

        $this->end_controls_section();
    }

    // Render widget output
    // Render widget output
    protected function render() {
        $settings = $this->get_settings_for_display();
        ?>
<div class="simple-widget">

<h2><?php echo esc_html( $settings['title'] ); ?></h2>

</div>
<?php }
}
```

 

## [#](#understanding-the-data-flow) Understanding the Data Flow

 

Let’s trace how data flows through a widget:

 

1. **User Interaction**: User sets “Products Per Page” to 12 in editor
2. **Setting Saved**: Value stored as `['products_per_page' => 12]`
3. **Render Called**: `render()` method executes
4. **Settings Retrieved**: `$settings = $this->get_settings_for_display()`
5. **Value Accessed**: `$settings['products_per_page']` returns 12
6. **Value Used**: Used in WP_Query to limit products

 

## [#](#common-mistakes-to-avoid) Common Mistakes to Avoid

 

### [#](#mistake-1%3A-forgetting-required-methods) Mistake 1: Forgetting Required Methods

 

```
// Bad - Missing required methods
class My_Widget extends \Elementor\Widget_Base {
    protected function render() {
        echo 'Hello';
    }
}

// Good - All required methods present
class My_Widget extends \Elementor\Widget_Base {
    public function get_name() { return 'my_widget'; }
    public function get_title() { return 'My Widget'; }
    public function get_icon() { return 'eicon-posts-grid'; }
    public function get_categories() { return ['general']; }
    protected function render() { echo 'Hello'; }
}
```

 

### [#](#mistake-2%3A-not-escaping-output) Mistake 2: Not Escaping Output

 

```
// Bad - No escaping
echo '<div class="' . $class . '">' . $content . '</div>';

// Good - Properly escaped
echo '<div class="' . esc_attr( $class ) . '">' . esc_html( $content ) . '</div>';
```

 

### [#](#mistake-3%3A-using-echo-in-register_controls()) Mistake 3: Using echo in register_controls()

 

```
// Bad - Don't output in register_controls()
protected function register_controls() {
    echo 'Adding controls';  // Wrong!
    $this->add_control(...);
}

// Good - Only define controls
protected function register_controls() {
    $this->add_control(...);
}
```

 

## [#](#testing-your-understanding) Testing Your Understanding

 

To verify you understand these concepts, try answering:

 

1. What happens if you don’t implement `get_name()`?
2. Which method outputs HTML to the frontend?
3. Where do you define the widget’s settings panel?
4. How do you access user settings in the `render()` method?
5. What’s the difference between `protected` and `public` methods?

 

**Answers:**

 

1. PHP will throw a fatal error – it’s a required abstract method
2. The `render()` method
3. In the `register_controls()` method
4. Using `$this->get_settings_for_display()`
5. `public` methods can be called from outside the class, `protected` only from within the class or child classes

 

## [#](#summary) Summary

 

You now understand:

 

- The Elementor widget base class structure
- Required methods and their purposes
- How to define widget controls
- The widget lifecycle
- How settings flow from editor to frontend
- Best practices for widget development

 

## [#](#what%E2%80%99s-next%3F) What’s Next?

 

In Part 3: Building the Basic Widget Structure, we’ll apply this knowledge to create the foundation of our Collection Products widget. We’ll:

 

- Create the widget PHP files
- Implement the required methods
- Register the widget with Elementor
- Add basic controls
- Create a simple render method
- Test the widget in Elementor

 

## [#](#additional-resources) Additional Resources

 

- [Elementor Developers – Widget Structure](https://developers.elementor.com/docs/widgets/)
- [Elementor Controls Reference](https://developers.elementor.com/docs/controls/)
- [PHP Object-Oriented Programming](https://www.php.net/manual/en/language.oop5.php)

 

---

 

**Previous:** [← Part 1: Setting Up the Development Environment](https://sajdoko.com/blog/elementor-widget-part-1-setting-up-the-development-environment/)  
 **Next:** [Part 3: Building the Basic Widget Structure →](https://sajdoko.com/blog/elementor-widget-part-3-building-the-basic-widget-structure/)

  

- [Code](https://sajdoko.com/blog/category/wordpress/code/)
- [Development](https://sajdoko.com/blog/category/development/)
- [Elementor](https://sajdoko.com/blog/category/wordpress/elementor/)
- [WooCommerce](https://sajdoko.com/blog/category/wordpress/woocommerce/)
- [WordPress](https://sajdoko.com/blog/category/wordpress/)
- [Elementor Widget](https://sajdoko.com/blog/tag/elementor-widget/)

   

## Have a project in mind?

 

Write to us and we come back with a concrete estimate within one working day.

  [Contact us →](https://sajdoko.com/contact/)     

## Leave a comment [Cancel](https://sajdoko.com/blog/elementor-widget-part-2-understanding-elementor-widget-architecture/?format=markdown#respond)

      

## Recent Posts

- [Web and Software Development Costs in Albania: The International Buyer’s Strategic Guide](https://sajdoko.com/blog/website-costs-in-albania-international-perspective/)
- [Scaling WooCommerce Performance: Eliminating High-Traffic Checkout Bottlenecks](https://sajdoko.com/blog/scaling-woocommerce-performance-checkout-bottlenecks/)
- [The Hidden Cost of Cheap or “Free” Websites: Why €100 Web Design Costs €3,000 Later](https://sajdoko.com/blog/the-hidden-cost-of-cheap-or-free-websites/)
- [Building High-Concurrency Tour Booking Engines: Architecture Beyond Generic Plugins](https://sajdoko.com/blog/building-high-concurrency-tour-booking-engines/)
- [WordPress Malware Removal and Security Hardening: The Complete Emergency Recovery Guide](https://sajdoko.com/blog/wordpress-malware-removal-and-security-hardening/)

## Recent Comments

1. Alvaro on [Optimizing and Extending the Livewire Product Filter – Part 4](https://sajdoko.com/blog/optimizing-and-extending-the-livewire-product-filter/#comment-4)
2. [sajdoko](https://sajdoko.com) on [How to Set Up WordPress on localhost Using XAMPP: A Step-by-Step Guide](https://sajdoko.com/blog/how-to-install-wordpress-on-localhost-using-xampp-step-by-step-guide/#comment-3)
3. Roshni on [How to Set Up WordPress on localhost Using XAMPP: A Step-by-Step Guide](https://sajdoko.com/blog/how-to-install-wordpress-on-localhost-using-xampp-step-by-step-guide/#comment-2)
