Skip to content
This plugin is new and currently in beta. For the stable version, please use the previous version of the plugin.

UI Element

Overview

UI Elements are the foundational building blocks for creating custom user interface components in the Stripo Email Editor. They enable you to extend the editor's interface with custom inputs, pickers, dropdowns, and specialized interactive components that go beyond standard HTML form elements. UI Elements can be embedded within controls and settings panels to provide rich, interactive functionality tailored to your specific needs.

Purpose and Core Concepts

What is a UI Element?

A UI Element in the Stripo Extensions SDK is a reusable UI component that:

  • Defines custom interactive interface elements (color pickers, dropdowns, custom inputs, etc.)
  • Can be embedded within controls
  • Maintains state and responds to external attribute changes
  • Integrates seamlessly with the editor's control system
  • Provides lifecycle hooks for initialization and cleanup
  • Communicates value changes to parent controls

Built-in vs Custom UI Elements

The SDK provides a rich set of built-in UI elements for common use cases, but you can also create custom UI elements when you need specialized functionality:

Built-in UI Elements - Ready to use without additional configuration:

  • Buttons, checkboxes, radio buttons
  • Text inputs, textareas, date pickers
  • Color pickers, font family selectors
  • Dropdown selects, switchers, counters
  • Labels, messages, icons
  • And more (see the Built-in UI Elements table)

Custom UI Elements - When built-in elements don't meet your needs:

  • Brand-specific color palette pickers
  • Advanced file uploaders with preview
  • Custom merge tag selectors
  • Specialized input validators
  • Integration with external services
  • Complex multi-field components

Creating Custom UI Elements

To create a custom UI element, follow these steps:

  1. Create a class that extends the UIElement class
  2. Register your UI element with the ExtensionBuilder

UI element configuration requires:

  • Unique element identifier (getId())
    • This identifier is used as a tag name in control templates
    • It must be unique across all UI elements in the extension
  • HTML template structure (getTemplate())
  • Render logic (onRender())
    • Register all event listeners and other UI element-specific logic here
  • Cleanup logic (onDestroy())
    • Remove all event listeners and other UI element-specific logic here
  • Getter and setter methods to maintain UI element state from parent control
  • (Optional) Attribute update handler (onAttributeUpdated())
    • This method is called when an attribute of the UI element is updated from the parent control

Once registered, you can use your custom UI element in control templates by referencing its ID:

javascript
import {UIElement, ExtensionBuilder} from '@stripoinc/ui-editor-extensions';

class BrandColorPickerUIElement extends UIElement {
    getId() {
        return 'brand-color-picker';
    }

    getTemplate() {
        return `
            <div class="brand-color-picker-palette" style="display: flex; gap: 16px;">
                <button
                    data-value="#ff6f61" 
                    style="width: 30px; height: 30px; border-radius: 50%; border: none; background-color: #ff6f61; cursor: pointer;"
                    title="Coral"></button>
                <button
                    data-value="#43e97b" 
                    style="width: 30px; height: 30px; border-radius: 50%; border: none; background-color: #43e97b; cursor: pointer;"
                    title="Mint"></button>
                <button 
                    data-value="#5f72bd"
                    style="width: 30px; height: 30px; border-radius: 50%; border: none; background-color: #5f72bd; cursor: pointer;"
                    title="Violet"></button>
            </div>
        `;
    }

    onRender(container) {
        this.palette = container.querySelector('.brand-color-picker-palette');
        this.palette.addEventListener('click', this.paletteClickHandler.bind(this));
    }

    onDestroy() {
        this.palette.removeEventListener('click', this.paletteClickHandler.bind(this));
    }

    paletteClickHandler(e) {
        const selectedColor = e.target.getAttribute('data-value');
        
        // Trigger value change to parent control.
        // The 'setValue' method will be called automatically.
        this.api.triggerValueChange(selectedColor);
    }

    getValue() {
        return this.palette.querySelector('button.selected')?.getAttribute('data-value');
    }

    setValue(_value) {
        this.palette.querySelector('button.selected')?.classList.remove('selected');
        this.palette.querySelector(`button[data-value="${_value}"]`)?.classList.add('selected');
    }

    onAttributeUpdated(_name, _value) {
        // Handle attribute updates from parent control
        console.log(`Attribute updated: ${_name} = `, _value);
    }
}

class BrandControl extends Control {
    getId() {
        return 'brand-control';
    }

    getTemplate() {
        return `<brand-color-picker name="brandColorPicker"></brand-color-picker>`;
    }

    onRender() {
        this.api.onValueChanged('brandColorPicker', (newValue, oldValue) => {
            // Handle value changes from 'brandColorPicker' UI element
            console.log('brandColorPicker value changed:', newValue);
        });
    }

    onTemplateNodeUpdated(node) {
        // Update UI to reflect template state
        this.api.updateValues({
            'brandColorPicker': '#43e97b' // Get actual value from node
        });

        // Optionally, set UI element state with attribute values
        this.api.setUIEAttribute('brandColorPicker', 'preferred-color', '#53f97a');
    }
}

export default new ExtensionBuilder()
    .addUiElement(BrandColorPickerUIElement)
    .addControl(BrandControl)
    .build();

Built-in UI Elements

Supported Built-in UI Elements

The following image and table list all built-in UI elements available in the Stripo Extensions SDK:

Tag NameAliasDescriptionCommon Attributes
<ue-button>UIElementType.BUTTONButton element for triggering actionsname, disabled, caption, icon
<ue-check-buttons>UIElementType.CHECK_BUTTONSGroup of checkboxes as buttonsname, disabled, buttons
<ue-checkbox>UIElementType.CHECKBOXCheckbox input for boolean valuesname, disabled, caption
<ue-color>UIElementType.COLORColor picker for selecting colorsname, disabled
<ue-counter>UIElementType.COUNTERNumeric input with increment/decrement buttonsname, disabled, min-value, max-value, step
<ue-datepicker>UIElementType.DATEPICKERDate picker for selecting datesname, disabled, placeholder, min-date
<ue-expandable>UIElementType.EXPANDABLEExpandable/collapsible containername, expanded
<ue-icon>UIElementType.ICONIcon display elementname, img, src, title, width, height, image-class, hint, is-active, visibility, transform
<ue-label>UIElementType.LABELText label with optional hintname, text, hint
<ue-message>UIElementType.MESSAGEMessage box for displaying informationname, type
<ue-nested-control>UIElementType.NESTED_CONTROLContainer for nesting other controlsname, disabled, control-id
<ue-orderable>UIElementType.ORDERABLEReorderable list containername, icon, position
<ue-radio-buttons>UIElementType.RADIO_BUTTONSRadio button groupname, disabled, buttons
<ue-select>UIElementType.SELECTPICKERDropdown select pickername, disabled, searchable, multi-select, placeholder, items
<ue-switcher>UIElementType.SWITCHERToggle switcher for boolean valuesname, disabled
<ue-text>UIElementType.TEXTSingle-line text inputname, disabled, placeholder
<ue-textarea>UIElementType.TEXTAREAMulti-line text inputname, disabled, resizable, placeholder

Using Built-in UI Elements

All built-in UI elements can be used directly in your control templates without additional registration by referencing their tag names or aliases:

Simple Usage

javascript
class ComprehensiveControl extends Control {
    getId() {
        return 'comprehensive-control';
    }

    getTemplate() {
        return `
            <div class="container two-columns">
                <${UIElementType.LABEL} ${UEAttr.LABEL.text}="<ue-button>"></${UIElementType.LABEL}>
                <${UIElementType.COLOR} ${UEAttr.COLOR.name}="colorPicker"></${UIElementType.COLOR}>
            </div>`;
    }

    onRender() {
        // Listen for 'colorPicker' UI element value changes
        this.api.onValueChanged('colorPicker', (newValue, oldValue) => {
            this.api.getDocumentModifier()
                .modifyHtml(this.node.querySelector(`.custom-message-area`))
                .setStyle('background-color', newValue)
                .apply(new ModificationDescription(`Updated background color to ${newValue}`));
        })
    }

    onTemplateNodeUpdated(node) {
        this.node = node;

        // Set the value for 'colorPicker' UI element
        const element = node.querySelector('.custom-message-area');
        this.api.updateValues({
            'colorPicker': node.querySelector(`.custom-message-area`).getStyle('background-color')
        });
    }
}

Complex Usage

Expandable UI Element

Expandable UI Elements allow you to group multiple UI elements inside a collapsible section, making complex forms more organized and user-friendly. An expandable UI element is composed of three main parts:

  • <ue-expandable> — The main wrapper for the entire collapsible section
  • <ue-expandable-header> — The header area, typically containing the title, placed next to the expand/collapse toggle
  • <ue-expandable-content> — The container for the content that is shown or hidden when expanded or collapsed
javascript
class ExpandableControl extends Control {
    getTemplate() {
        return `
            <div class="container">
                <${UIElementType.EXPANDABLE}>
                    <${UIElementType.EXPANDABLE_HEADER}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Click to expand"></${UIElementType.LABEL}>
                    </${UIElementType.EXPANDABLE_HEADER}>
                    <${UIElementType.EXPANDABLE_CONTENT}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="First content row"></${UIElementType.LABEL}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Another row"></${UIElementType.LABEL}>
                    </${UIElementType.EXPANDABLE_CONTENT}>
                </${UIElementType.EXPANDABLE}>
            </div>`;
    }

    // Additional configuration should be here
}

Orderable UI Element

The Orderable UI Element allows users to reorder items within a list by dragging and dropping them. This component is useful when you need to provide a visual interface for changing the sequence of elements, such as reordering menu items, social links, or content blocks.

An orderable component is composed of three main parts:

  • <ue-orderable> — The main wrapper that manages the reorderable list
  • <ue-orderable-item> — Individual items that can be reordered
  • <ue-orderable-icon> — (Optional) Custom drag handle icon for individual items

Attributes

<ue-orderable> attributes:

  • name — (Required) Unique identifier for the orderable element
  • icon — (Optional) Icon name for the drag handle (default: system icon)
  • position — (Optional) Position of the drag handle icon. Values: TOP, LEFT (default: TOP)

<ue-orderable-item> attributes:

  • name — (Optional) Unique identifier for the item. If not provided, items will be automatically named as item1, item2, etc.

<ue-orderable-icon> attributes:

  • icon — (Required) Icon name to override the parent's drag handle icon for this specific item

Value Format

The orderable element returns an array of item names representing their current order:

typescript
// Example value when items are reordered
['item2', 'item1', 'item3']

Usage Examples

Example 1: Default Icon with Top Positioning

Use the default drag handle icon positioned at the top of each item. Items without an explicit name attribute will be automatically named as item1, item2, etc.

javascript
class OrderableControl extends Control {
    getTemplate() {
        return `
            <div class="container">
                <${UIElementType.ORDERABLE} name="orderableUIElement">
                    <${UIElementType.ORDERABLE_ITEM}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Item 1"></${UIElementType.LABEL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                    <${UIElementType.ORDERABLE_ITEM}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Item 2"></${UIElementType.LABEL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                </${UIElementType.ORDERABLE}>
            </div>`;
    }

    onRender() {
        this.api.onValueChanged('orderableUIElement', (newValue, oldValue) => {
            // oldValue -> ['item1', 'item2']
            // newValue -> ['item2', 'item1']
            console.log('Items reordered:', newValue);
        });
    }

    // Additional configuration should be here
}

Example 2: Custom Icon with Named Items

Use a custom icon (dots-3) for the drag handle while explicitly naming each item for better tracking:

javascript
class OrderableControl extends Control {
    getTemplate() {
        return `
            <div class="container">
                <${UIElementType.ORDERABLE} name="orderableUIElement" icon="dots-3">
                    <${UIElementType.ORDERABLE_ITEM} name="orderableItem1">
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Item 1"></${UIElementType.LABEL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                    <${UIElementType.ORDERABLE_ITEM} name="orderableItem2">
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Item 2"></${UIElementType.LABEL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                </${UIElementType.ORDERABLE}>
            </div>`;
    }

    onRender() {
        this.api.onValueChanged('orderableUIElement', (newValue, oldValue) => {
            // oldValue -> ['orderableItem1', 'orderableItem2']
            // newValue -> ['orderableItem2', 'orderableItem1']
            console.log('Items reordered:', newValue);
        });
    }

    // Additional configuration should be here
}

Example 3: Left-Positioned Icon with Per-Item Customization

Position the drag handle on the left side and override the icon for specific items:

javascript
class OrderableControl extends Control {
    getTemplate() {
        return `
            <div class="container">
                <${UIElementType.ORDERABLE} name="orderableUIElement" icon="dots-3" position="LEFT">
                    <${UIElementType.ORDERABLE_ITEM} name="orderableItem1">
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Item 1"></${UIElementType.LABEL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                    <${UIElementType.ORDERABLE_ITEM} name="orderableItem2">
                        <${UIElementType.ORDERABLE_ICON} icon="dots-6"></${UIElementType.ORDERABLE_ICON}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Item 2"></${UIElementType.LABEL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                </${UIElementType.ORDERABLE}>
            </div>`;
    }

    onRender() {
        this.api.onValueChanged('orderableUIElement', (newValue, oldValue) => {
            // oldValue -> ['orderableItem1', 'orderableItem2']
            // newValue -> ['orderableItem2', 'orderableItem1']
            console.log('Items reordered:', newValue);
        });
    }

    // Additional configuration should be here
}

Key Concepts

  • Automatic Naming: If items don't have a name attribute, they're automatically assigned names like item1, item2, etc.
  • Icon Positioning: The position attribute controls where the drag handle appears (TOP or LEFT)
  • Per-Item Icons: Use <ue-orderable-icon> within an item to override the parent's drag handle icon
  • Value Tracking: The component's value is always an array of item names in their current order
  • Nested Content: Items can contain any combination of UI elements, not just labels

Nested UI Element

The Nested UI Element allows you to embed one control inside another control's template. This enables powerful composition patterns such as grouping multiple controls within an Expandable section or creating dynamic lists of controls with the Orderable component.

Attributes:

  • control-id — (Required) The unique identifier of the control to be nested

Key Use Cases:

  • Grouping multiple controls in expandable sections
  • Creating reorderable lists of controls
  • Building complex, hierarchical control layouts

Usage Example: Nested Control in Expandable Section

javascript
class ExpandableNestedControl extends Control {
    getId() {
        return 'expandable-nested-control';
    }

    getTemplate() {
        return `
            <div class="container">
                <${UIElementType.EXPANDABLE}>
                    <${UIElementType.EXPANDABLE_HEADER}>
                        <${UIElementType.LABEL} ${UEAttr.LABEL.text}="Advanced Settings"></${UIElementType.LABEL}>
                    </${UIElementType.EXPANDABLE_HEADER}>
                    <${UIElementType.EXPANDABLE_CONTENT}>
                    
                        <${UIElementType.NESTED_CONTROL} 
                            ${UEAttr.NESTED_CONTROL.controlId}="my-custom-control-1-id">
                        </${UIElementType.NESTED_CONTROL}>
                        
                        <${UIElementType.NESTED_CONTROL} 
                            ${UEAttr.NESTED_CONTROL.controlId}="my-custom-control-2-id">
                        </${UIElementType.NESTED_CONTROL}>
                        
                    </${UIElementType.EXPANDABLE_CONTENT}>
                </${UIElementType.EXPANDABLE}>
            </div>`;
    }

    // Additional configuration should be here
}

Usage Example: Orderable List of Controls

javascript
class OrderableNestedControlList extends Control {
    getId() {
        return 'orderable-nested-list';
    }

    getTemplate() {
        return `
            <div class="container">
                <${UIElementType.ORDERABLE} name="controlsList">
                    <${UIElementType.ORDERABLE_ITEM} name="item1">
                        <${UIElementType.NESTED_CONTROL} 
                            ${UEAttr.NESTED_CONTROL.controlId}="my-custom-control-1-id">
                        </${UIElementType.NESTED_CONTROL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                    
                    <${UIElementType.ORDERABLE_ITEM} name="item2">
                        <${UIElementType.NESTED_CONTROL} 
                            ${UEAttr.NESTED_CONTROL.controlId}="my-custom-control-2-id">
                        </${UIElementType.NESTED_CONTROL}>
                    </${UIElementType.ORDERABLE_ITEM}>
                </${UIElementType.ORDERABLE}>
            </div>`;
    }

    onRender() {
        this.api.onValueChanged('controlsList', (newValue, oldValue) => {
            // Handle reordering of nested controls
            console.log('Controls reordered:', newValue);
        });
    }

    // Additional configuration should be here
}

Overriding Built-in UI Elements

The UIElementTagRegistry allows you to replace built-in UI elements with custom implementations throughout your extension. This is particularly useful when you want to:

  • Apply consistent branding to all UI elements
  • Add custom functionality to standard elements
  • Integrate with external services
  • Enforce specific validation or formatting rules

Creating a Tag Registry

To override built-in UI elements:

  1. Create a class that extends UIElementTagRegistry
  2. Implement the registerUiElements() method
  3. Map tags to your custom UI element IDs
  4. Register the registry with ExtensionBuilder
javascript
import {UIElementTagRegistry, UIElementType, ExtensionBuilder} from '@stripoinc/ui-editor-extensions';

class CustomTagRegistry extends UIElementTagRegistry {
    registerUiElements(uiElementsTagsMap) {
        // Override the built-in color picker with custom implementation
        uiElementsTagsMap[UIElementType.COLOR] = 'brand-color-picker';
    }
}

export default new ExtensionBuilder()
    .addUiElement(BrandColorPickerUIElement)
    .withUiElementTagRegistry(CustomTagRegistry)
    .build();

UI Element API

The UI Element API, accessible through this.api, provides method to notify the parent control when the UI element's value changes, ensuring proper synchronization and state management.

For complete API reference, see UIElementApi.