Melios
Blog

A very basic Page Builder component

Let's create a skeleton for your next component. And mine too!

Let's create a component named skeleton. To have a fully working example, we will need six files:

module_name
└── view/adminhtml
          ├── pagebuilder
          │   └── content_type
          │       └── skeleton.xml
          ├── layout
          │   └── pagebuilder_skeleton_form.xml
          ├── ui_component
          │   └── pagebuilder_skeleton_form.xml
          └── web
              ├── js
              │   └── skeleton.js
              └── template
                  └── skeleton
                      ├── master.html
                      └── preview.html

Let's start!

Adding component to the page builder's sidebar

To add the new component to the sidebar, we need to declare it in the content_type file of our module. This file describes the component and its possible appearances. Each component must have at least one appearance.

view/adminhtml/pagebuilder/content_type/skeleton.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_PageBuilder:etc/content_type.xsd">
  <type name="skeleton"
        label="Skeleton"
        component="Magento_PageBuilder/js/content-type"
        preview_component="Vendor_Module/js/skeleton"
        form="pagebuilder_skeleton_form"
        menu_section="media"
        icon="icon-pagebuilder-row"
        sortOrder="40"
        translate="label">
    <children default_policy="deny"/>
    <appearances>
      <appearance default="true"
                  name="default"
                  preview_template="Vendor_Module/skeleton/preview"
                  master_template="Vendor_Module/skeleton/master"
                  reader="Magento_PageBuilder/js/master-format/read/configurable">
        <elements>
          <element name="main">
            <style name="text_align" source="text_align"/>
            <style name="border" source="border_style" converter="Magento_PageBuilder/js/converter/style/border-style"/>
            <style name="border_color" source="border_color"/>
            <style name="border_width" source="border_width" converter="Magento_PageBuilder/js/converter/style/border-width"/>
            <style name="border_radius" source="border_radius" converter="Magento_PageBuilder/js/converter/style/remove-px"/>
            <style name="display" source="display" converter="Magento_PageBuilder/js/converter/style/display" preview_converter="Magento_PageBuilder/js/converter/style/preview/display"/>
            <style name="margins" storage_key="margins_and_padding" reader="Magento_PageBuilder/js/property/margins" converter="Magento_PageBuilder/js/converter/style/margins"/>
            <style name="padding" storage_key="margins_and_padding" reader="Magento_PageBuilder/js/property/paddings" converter="Magento_PageBuilder/js/converter/style/paddings"/>
            <attribute name="name" source="data-content-type"/>
            <attribute name="appearance" source="data-appearance"/>
            <css name="css_classes"/>
          </element>
        </elements>
      </appearance>
    </appearances>
  </type>
</config>

Now, our component is visible in the sidebar's Media section and you can try dragging it into the page! However, it won't work.

Additionally, you may notice the 404 error in the browser console on initial page load. This happens because Magento preloads all preview templates described in content_type files but we haven't created preview.html yet.

Let's move on!

Allow dragging the component into the content

At this point, we need to create three files:

view/adminhtml/web/js/skeleton.js
define([
    'Magento_PageBuilder/js/content-type/preview',
    'Magento_PageBuilder/js/content-type-menu/hide-show-option'
], function (Parent, HideShow) {
    'use strict';

    return class Skeleton extends Parent {
        retrieveOptions() {
            const options = super.retrieveOptions();

            // Add "hide/show" icon
            options.hideShow = new HideShow({
                preview: this,
                icon: HideShow.showIcon,
                title: HideShow.showText,
                action: this.onOptionVisibilityToggle,
                classes: ['hide-show-content-type'],
                sort: 40
            });

            return options;
        }
    };
});
view/adminhtml/web/template/skeleton/preview.html
<div class="pagebuilder-content-type pagebuilder-skeleton" event="{ mouseover: onMouseOver, mouseout: onMouseOut }, mouseoverBubble: false">
    <render args="getOptions().template"></render>
    <div attr="data.main.attributes" css="data.main.css" ko-style="data.main.style">Skeleton</div>
</div>
view/adminhtml/web/template/skeleton/master.html
<div attr="data.main.attributes" css="data.main.css" ko-style="data.main.style">Skeleton</div>

Okay, you can now try inserting the component into the page.

Don't forget to insert the Row element first! Alternatively, install Melios Page Builder — it will automatically wrap the component in a Row when needed.

Now, click the "Edit" icon... And it doesn't work! Let's fix it!

Allow editing inserted component

We need to create two more files to fix the form rendering:

view/adminhtml/layout/pagebuilder_skeleton_form.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="pagebuilder_skeleton_form"/>
        </referenceContainer>
    </body>
</page>
view/adminhtml/ui_component/pagebuilder_skeleton_form.xml
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd" extends="pagebuilder_base_form">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">pagebuilder_skeleton_form.pagebuilder_skeleton_form_data_source</item>
        </item>
        <item name="label" xsi:type="string" translate="true">Skeleton</item>
    </argument>
    <settings>
        <deps>
            <dep>pagebuilder_skeleton_form.pagebuilder_skeleton_form_data_source</dep>
        </deps>
        <namespace>pagebuilder_skeleton_form</namespace>
    </settings>
    <dataSource name="pagebuilder_skeleton_form_data_source">
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_PageBuilder/js/form/provider</item>
            </item>
        </argument>
        <dataProvider name="pagebuilder_skeleton_form_data_source" class="Magento\PageBuilder\Model\ContentType\DataProvider">
            <settings>
                <requestFieldName/>
                <primaryFieldName/>
            </settings>
        </dataProvider>
    </dataSource>
    <fieldset name="appearance_fieldset" sortOrder="10" component="Magento_PageBuilder/js/form/element/dependent-fieldset">
        <settings>
            <imports>
                <link name="hideFieldset">${$.name}.appearance:options</link>
                <link name="hideLabel">${$.name}.appearance:options</link>
            </imports>
        </settings>
        <field name="appearance" formElement="select" sortOrder="10">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="default" xsi:type="string">default</item>
                </item>
            </argument>
            <formElements>
                <select>
                    <settings>
                        <options>
                            <option name="0" xsi:type="array">
                                <item name="value" xsi:type="string">default</item>
                                <item name="label" xsi:type="string" translate="true">Default</item>
                            </option>
                        </options>
                    </settings>
                </select>
            </formElements>
        </field>
    </fieldset>
</form>

That's all! Our new component is fully functional in both Backend and Frontend!

You can now explore built-in Magento components to learn more and extend your basic component with additional fields and functionality.