$npx -y skills add Lonsdale201/wp-agent-skills --skill elementor-dynamic-tag-fieldsBuild the body of an Elementor Dynamic Tag — choose Tag
| 1 | # Elementor: Dynamic Tag types, fields & fallback |
| 2 | |
| 3 | The body of a dynamic tag has three decisions: **which base class** (`Tag` vs `Data_Tag`), **which categories** it produces (so the right controls accept it), and **which settings fields** the editor shows. Plus the easily-missed **fallback** asymmetry between the two base classes. This skill assumes the tag is already being registered — see `elementor-dynamic-tag-register` for that. |
| 4 | |
| 5 | ## Decision 1 — `Tag` vs `Data_Tag` |
| 6 | |
| 7 | | | `Tag` | `Data_Tag` | |
| 8 | |---|---|---| |
| 9 | | Extend | `\Elementor\Core\DynamicTags\Tag` | `\Elementor\Core\DynamicTags\Data_Tag` | |
| 10 | | You implement | `render()` — **echo** the output | `get_value( array $options = [] )` — **return** the value | |
| 11 | | `get_content_type()` | `'ui'` (final) | `'plain'` (final) | |
| 12 | | Use for | rendered text/HTML fragments (price, reading time, a badge) | structured values consumed by a control — an image array `[ 'id' => …, 'url' => … ]`, a URL string, a color | |
| 13 | |
| 14 | Verified: `Tag::get_content()` does `ob_start(); $this->render(); $value = ob_get_clean();` ([tag.php:30-37](tag.php)) and `get_content_type()` returns `'ui'` ([tag.php:64](tag.php)). `Data_Tag` declares `abstract protected function get_value()` ([data-tag.php:25](data-tag.php)), returns it directly from `get_content()` ([data-tag.php:43-45](data-tag.php)), and `get_content_type()` returns `'plain'` ([data-tag.php:31](data-tag.php)). |
| 15 | |
| 16 | Rule of thumb: if the value feeds a **MEDIA / IMAGE / URL / COLOR** control (the control needs a structured value, not printed markup), use `Data_Tag`. If it feeds a **TEXT** context (it's printed inline), use `Tag`. |
| 17 | |
| 18 | ```php |
| 19 | // Tag — echoes |
| 20 | class Reading_Time extends Tag { |
| 21 | public function render(): void { |
| 22 | echo esc_html( $this->compute() . ' min' ); // print, don't return |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | // Data_Tag — returns |
| 27 | class Featured_Image_Fallback extends Data_Tag { |
| 28 | protected function get_value( array $options = [] ) { |
| 29 | $id = get_post_thumbnail_id(); |
| 30 | if ( $id ) { |
| 31 | return [ 'id' => $id, 'url' => wp_get_attachment_image_src( $id, 'full' )[0] ]; |
| 32 | } |
| 33 | return $this->get_settings( 'fallback' ); // see "Fallback" below |
| 34 | } |
| 35 | } |
| 36 | ``` |
| 37 | |
| 38 | ## Decision 2 — categories (what value the tag produces) |
| 39 | |
| 40 | `get_categories()` returns one or more **category constants** from `Elementor\Modules\DynamicTags\Module`. Categories declare the *kind* of value the tag emits; a control accepts a tag only when their categories overlap, so the editor shows your tag only under compatible controls. |
| 41 | |
| 42 | Verified constants ([modules/dynamic-tags/module.php:31-76](module.php)): |
| 43 | |
| 44 | | Constant | Value | Typical use | |
| 45 | |---|---|---| |
| 46 | | `TEXT_CATEGORY` | `'text'` | printed strings (most `Tag`s) | |
| 47 | | `URL_CATEGORY` | `'url'` | link fields | |
| 48 | | `IMAGE_CATEGORY` | `'image'` | image controls | |
| 49 | | `MEDIA_CATEGORY` | `'media'` | media (image/video) controls | |
| 50 | | `POST_META_CATEGORY` | `'post_meta'` | meta-field contexts | |
| 51 | | `GALLERY_CATEGORY` | `'gallery'` | gallery controls | |
| 52 | | `NUMBER_CATEGORY` | `'number'` | number controls | |
| 53 | | `COLOR_CATEGORY` | `'color'` | color controls | |
| 54 | | `DATETIME_CATEGORY` | `'datetime'` | date/time controls | |
| 55 | | `SVG_CATEGORY` | `'svg'` | inline-SVG / icon controls | |
| 56 | |
| 57 | A tag may declare several — Pro's `Post_Custom_Field` returns `[ TEXT, URL, POST_META, COLOR, DATETIME, MEDIA ]` because a meta value can drive any of those controls. Reference the constant, never the bare string, so a renamed value can't break you. |
| 58 | |
| 59 | ```php |
| 60 | use Elementor\Modules\DynamicTags\Module as TagsModule; |
| 61 | |
| 62 | public function get_categories(): array { |
| 63 | return [ TagsModule::TEXT_CATEGORY ]; |
| 64 | } |
| 65 | ``` |
| 66 | |
| 67 | ## Decision 3 — settings fields via `register_controls()` |
| 68 | |
| 69 | Add the tag's config |