Elementor V3 widget controls
SkillMediaDesigns and reviews built-in controls for classic Elementor `Widget_Base` widgets: content/style sections, control value shapes, responsive and group controls, CSS selectors, conditions, dynamic tags, URL/media/icons values, repeaters, inline editing, and safe PHP rendering. Use when code calls `start_controls_section()`, `add_control()`, `add_responsive_control()`, `add_group_control()`, creates `Repeater`, uses `selectors` or `condition`, or reads `get_settings_for_display()`. Excludes custom control classes and Atomic/V4 controls.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Elementor V3 widget controls skill
What this skill tells your AI
The instructions your AI receives, as published by lonsdale201/wp-agent-skills in elementor/elementor-v3-widget-controls/SKILL.md and read by ahel’s review.
Build editor panels from Elementor's built-in classic controls and couple each saved value to safe, predictable rendering. “V3” means the established Widget_Base / Controls_Stack model even when the installed plugin is Elementor 4.x. Do not apply these arrays to Atomic Widgets / Editor V4.
This skill does not create custom control types. Prefer a built-in control or a well-defined fallback; a custom-control integration is a separate lifecycle, asset, and compatibility problem.
When to use this skill
- Add or review
register_controls()in aWidget_Basesubclass. - Choose between regular, responsive, group, repeater, media, URL, or icon controls.
- Use
selectors,selectors_dictionary,prefix_class,condition, orconditions. - Diagnose a control that saves one shape but
render()expects another. - Enable dynamic tags or expose selected settings to widget JavaScript.
- Render repeater rows, responsive values, links, icons, or editor-inline text.
- Audit whether Elementor controls are being mistaken for sanitizers.
Read references/built-in-controls-and-patterns.md when implementing value shapes, selector tokens, group controls, repeaters, or a full example. Pair this skill with elementor-v3-widget-development for bootstrap, registration, assets, caching, and frontend lifecycle.
Workflow
1. Start from output and data shape
Before adding panel fields, write down:
- The semantic output and accessibility behavior.
- The exact saved value shape: scalar, compound array, list, or responsive variants.
- The final output context and validation allowlist.
- Whether a style can be expressed through Elementor selectors or needs PHP/JS.
- Whether the value may use a dynamic tag.
Do not choose a control by appearance alone. A URL, MEDIA, ICONS, SLIDER, DIMENSIONS, and REPEATER each returns a structured array, not a string.
2. Put controls in explicit sections
Classic widget controls must be inside a section:
$this->start_controls_section(
'section_content',
[
'label' => esc_html__( 'Content', 'acme' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// add_control(), add_responsive_control(), add_group_control()...
$this->end_controls_section();
- Use stable, prefixed IDs when collision or future injection is plausible.
- Use
TAB_CONTENTfor data/behavior andTAB_STYLEfor presentation. - Do not nest sections;
Controls_Stackrejects controls outside a section and section misuse can terminate panel construction. - Keep editor labels/descriptions translated and concise. Never translate IDs, option keys, CSS selectors, or stored values.
- Use headings, separators, popovers, and tabs only to clarify a real grouping.
3. Choose the smallest built-in control that matches the value
| Need | Control | Render-time shape/check |
|---|---|---|
| Short plain input | TEXT, NUMBER, TEXTAREA | scalar; validate/escape for use |
| Restricted choice | SELECT, CHOOSE, SWITCHER | scalar; re-check against allowlist |
| Rich content | WYSIWYG | string; use an explicit HTML policy |
| Link | URL | url, is_external, nofollow, custom_attributes |
| Image/file | MEDIA | id, url, size; prefer attachment APIs when ID exists |
| Icon | ICONS | value, library; render via Icons_Manager |
| Size | SLIDER | size, unit, optionally sizes |
| Box values | DIMENSIONS | top/right/bottom/left/unit/isLinked |
| Multiple images | GALLERY | list of attachment-like arrays |
| Repeated rows | REPEATER | list of row maps, each with stable _id |
Use RAW_HTML, HEADING, DIVIDER, and POPOVER_TOGGLE as panel UI, not as content storage. Do not put secrets or authorization state in any control: Elementor document settings are content data, not a confidential store.
4. Use display settings for rendering
$settings = $this->get_settings_for_display();
This returns active settings after conditions and dynamic-tag parsing. It does not grant permission to run arbitrary shortcodes; process shortcodes only through an explicit, intentional renderer. Use raw $this->get_settings() only for a specifically documented need such as inspecting stored configuration before dynamic resolution.
Control definitions do not establish a security boundary:
- A
SELECToption list does not prevent an imported/filtered/database value outside the list. - A numeric UI range does not prove the saved value is in range.
- Dynamic tags can replace a value at display time.
- A conditional hidden control may still exist in raw document data; the display value can be
nullwhen inactive.
Validate allowed HTML tags, element names, CSS classes, IDs, numbers, URLs, attachment visibility, and business permissions in the code that consumes them. Escape at final output.
5. Let selectors handle deterministic styles
Use selectors for styles fully derived from a control:
$this->add_responsive_control(
'gap',
[
'label' => esc_html__( 'Gap', 'acme' ),
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem' ],
'range' => [ 'px' => [ 'min' => 0, 'max' => 100 ] ],
'selectors' => [
'{{WRAPPER}} .acme-card__list' => 'gap: {{SIZE}}{{UNIT}};',
],
]
);
- Anchor selectors at
{{WRAPPER}}to prevent cross-widget leakage. - Use
{{VALUE}},{{SIZE}}, and{{UNIT}}only where the chosen control supplies them. - Use
selectors_dictionaryto map stored choices to CSS values instead of embedding arbitrary CSS. - Use
{{CURRENT_ITEM}}for per-row repeater styling. - Prefer
add_responsive_control()only when per-device values make sense; do not manually guess generated breakpoint suffixes. - Use
prefix_classonly with a tight option allowlist and a namespaced prefix.
Selector output is presentation, not permission enforcement or server-side validation. Do not interpolate arbitrary editor text into property names, selectors, at-rules, or unrestricted declarations.
6. Use conditions as editor UX, not runtime authorization
Simple equality/membership belongs in condition; compound logic belongs in conditions:
'condition' => [ 'show_icon' => 'yes' ],
'conditions' => [
'relation' => 'or',
'terms' => [
[ 'name' => 'columns', 'operator' => '>', 'value' => 1 ],
[ 'name' => 'columns', 'operator' => '===', 'value' => '' ],
],
],
Use supported operators only. Conditions change panel visibility and active settings; they do not authorize output or delete stored values. Inside a repeater, an inner field may depend on another field in the same row. Do not make an inner field depend on an outer/main control; Elementor documents that cross-level dependency as unsupported.
7. Prefer group controls for coherent CSS features
Use add_group_control() with official types such as Typography, Background, Border, Box Shadow, Text Shadow, Text Stroke, CSS Filter, or Image Size. Give each group a unique name and its target selector.
Do not manually recreate the group's internal control IDs or read guessed keys. Let the group generate selectors, or use its documented renderer/helper (for example image-size output) where required.
8. Render repeaters with stable keys
Create fields with new \Elementor\Repeater() and pass $repeater->get_controls() to a REPEATER control. get_fields() is deprecated.
At render time:
- Confirm the setting is an array.
- Validate each row field independently.
- Build a unique attribute/link key per row with
get_repeater_setting_key()or a namespaced index key. - Use the row
_id/{{CURRENT_ITEM}}contract for row-specific styling; do not use array order as a persistent identity. - Bound any query or remote work driven by rows; avoid N+1 lookups.
For large remote/post/product/user datasets, do not preload thousands of SELECT2 options. Apply elementor-dynamic-tag-ajax-select for the Pro AJAX Query Control plus a free-safe manual-ID fallback.
9. Expose only intentional frontend settings
frontend_available => true makes a control available to frontend handlers; it is not a secure transport. Expose only values required by JS, never secrets, nonces intended for another action, capability decisions, private IDs, or raw privileged data. Re-authorize every server request made by the handler.
Critical rules
- Keep classic control arrays out of Atomic/V4 classes.
- Put widget controls inside balanced sections; do not nest sections.
- Match the render code to the control's actual scalar/compound/list value shape.
- Use
get_settings_for_display()for normal rendering and handle inactivenullvalues. - Treat every setting as untrusted at output, including select values and dynamic tags.
- Anchor style selectors at
{{WRAPPER}}and whitelist class/tag/CSS choices. - Treat conditions as panel UX, never authorization.
- Use
get_controls()for repeater fields and stable per-row render keys. - Render URL, media, and icon values through their dedicated APIs.
- Keep large datasets asynchronous or use a bounded manual-ID fallback.
Review checks
- Every control is in the right tab/section and has a stable unique ID.
- Defaults match the control's real value shape and render assumptions.
- Responsive settings are not read as one unsuffixed scalar in custom PHP/JS logic.
- Selector placeholders match the control shape and remain wrapper-scoped.
- Conditions reference controls at a supported scope and inactive values are handled.
- Dynamic-tag eligibility matches the semantic value type.
- Output validation/escaping exists independently of the editor UI.
- Repeaters have bounded work, stable keys, safe empty state, and no N+1 query.
frontend_availablereveals no sensitive data.- Style-control tests account for Elementor's optimized split stack; query a known control by ID instead of treating a context-dependent bulk
get_controls()list as complete.
Cross-references
- Run
elementor-v3-widget-developmentfor addon bootstrap, widget registration, rendering, assets, JS lifecycle, and caching. - Run
elementor-dynamic-tag-ajax-selectfor large dataset selectors and Pro/free degradation. - Run
elementor-experiments-and-markupforICONSoutput and optimized wrapper behavior.
What this skill does NOT cover
- Creating or registering a custom Elementor control class.
- Atomic Widgets / Editor V4 prop types, controls, or style schema.
- Pro Forms fields, nested elements, skins, documents, or Theme Builder controls.
- Generic WordPress form processing, persistence, REST authorization, or business rules.
References
- Built-in control catalog, value shapes, group controls, selectors, repeater pattern, and escaping matrix:
references/built-in-controls-and-patterns.md. - Official editor controls documentation: https://developers.elementor.com/docs/editor-controls/
- Official conditional display documentation: https://developers.elementor.com/docs/editor-controls/conditional-display/
- Official repeater control documentation: https://developers.elementor.com/docs/editor-controls/control-repeater/
- Official widget rendering documentation: https://developers.elementor.com/docs/widgets/
- Verified Elementor Free 4.2.3 source paths:
includes/managers/controls.phpincludes/base/controls-stack.phpincludes/controls/includes/controls/groups/includes/elements/repeater.phpincludes/base/element-base.phpincludes/base/widget-base.phpincludes/widgets/heading.phpincludes/widgets/icon-list.php
Signals
- GitHub stars
- 22
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
elementor-v3-widget-controls- Source
- github.com/lonsdale201/wp-agent-skills