How to: Use Editor Controls

The Editor Controls allows theme developers to create user-friendly interfaces for the sellers to edit their site without requiring them to manually manipulate JSON files. This is an optional but additive feature that will allow less technical sellers edit their site with less risk of breaking the data structures required to render the page.

Controls can be added to page template and section templates and will only be used within the context of the editor. It will have no direct effect on the performance of the published site as it is ignored during render.

Basic example

Controls consist of small primitive building blocks that manipulate data resources defined in the page files. Take for example the following controls that allows the seller to manipulate the title and subtitle of this “Header section”:

page.json

{
  "header": {
    "props": {
      "title": "Title",
      "description": "Subtitle"
    }
  }
}

header.html.twig

<h1>{{ title }}</h1>
<h2>{{ description }}</h2>

{% schema %}
{
  "title": {
      "type": "string"
  },
  "description": {
      "type": "string"
  }
}
{% endschema %}

{% editor %}
{
  "name": "Header section",
  "controls": [
    {
      "control": "input",
      "label": "Title",
      "value": {
        "$ref": "#/schema/title"
      }
    },
    {
      "control": "textarea",
      "label": "Description",
      "value": {
        "$ref": "#/schema/description"
      }
    }
  ]
}
{% endeditor %}

Basic example

The example above will render two editing controls in the editor. Each control is defined to read and write from two properties via the value tag with a JSON pointer to the respective schema value. When the seller makes any changes to these controls it is automatically saved to the page and the changes are reflected in the editor preview.

Value references

Many of the controls accept a value reference, in most cases you’ll want to use a JSON pointer to reference a value in the schema tag. This enables the editor to dynamically read and write to the correct property in the page file:

{% schema %}
{
  "title": {
      "type": "string"
  }
}
{% endschema %}
{% editor %}
{
  "name": "Header section",
  "controls": [
    {
      "control": "input",
      "label": "Title",
      "value": {
        "$ref": "#/schema/title"
      }
    }
  ]
}
{% endeditor %}

You can also hardcode a value to the control as long as it matches the expected data format but it isn’t particularly useful as it will not save the data when changes are made:

{% editor %}
{
  "name": "Header section",
  "controls": [
    {
      "control": "input",
      "label": "Title",
      "value": "My title"
    }
  ]
}
{% endeditor %}

The above will render an input control with the value “My title”, but if a seller changes this value it will not be saved to the page file since we never instructed the editor where to save the data.

Resource controls

Complex controls are also available to allow the sellers manipulate more configuration heavy resource types like the Image, CategoryList, and ItemList.

page.json

{
  "itemSection": {
    "props": {
      "items": {
        "filters": {
          "ids": [
            "14",
            "13",
            "12",
            "10"
          ],
          "square_online_id": true
        }
      }
    }
  }
}

item.html.twig

<b>Items</b>
<ul>
{% for item in items %}
  <li>
    <h3>{{ item.name }}</h3>
    <img src="{{ item.images[0] }}" />
  </li>
{% endfor %}
</ul>

{% schema %}
{
    "items": {
        "type": "item-list"
    }
}
{% endschema %}

{% editor %}
{
    "name": "Items section",
    "controls": [
        {
            "control": "item-list-chooser",
            "label": "Item chooser",
            "value": {
                "$ref": "#/schema/items"
            }
        }
    ]
}
{% endeditor %}

Resource controls

Organizing controls

It is good practice to give the sellers a great editing experience, organizing and grouping your controls can help create a mental model about around what is related and how some controls will interact with certain components.

For example, we’ll use the group control to group different primitive controls to create a singular image editing experience:

page.json

{
  "heroSection": {
    "props": {
      "tagline": "Hats",
      "hero": {
        "alt": "Yellow hat",
        "link": {
          "id": "14",
          "type": "product"
        },
        "image": {
          "src": "/yellow_hat.jpg",
          "name": "yellow_hat.jpg",
          "width": 3000,
          "height": 3750,
          "srcset": [],
          "attribution": []
        }
      }
    }
  }
}

hero.html.twig

<div>
  <h1>{{ title }}</h1>
  {% if hero.link is defined and hero.image is defined %}
    <a href="{{ link(hero.link) }}">
      <img alt="{{ hero.alt }}" src="{{ hero.image.src }}" />
    </a>
  {% elseif hero.image is defined %}
    <img alt="{{ hero.alt }}" src="{{ hero.image.src }}" />
  {% endif %}
</div>

{% schema %}
{
  "title": {
    "type": "string"
  },
  "hero": {
    "type": "array"
  }
}
{% endschema %}

{% editor %}
{
  "name": "Hero section",
  "controls": [
    {
      "control": "input",
      "label": "Title",
      "value": {
        "$ref": "#/schema/title"
      }
    },
    {
      "control": "group",
      "label": "Hero image",
      "children": [
        {
          "control": "image",
          "value": {
            "$ref": "#/schema/hero/source"
          }
        },
        {
          "control": "input",
          "label": "Alt",
          "value": {
            "$ref": "#/schema/hero/alt"
          }
        },
        {
          "control": "linker",
          "label": "Link",
          "value": {
            "$ref": "#/schema/hero/link"
          }
        }
      ]
    }
  ]
}
{% endeditor %}

Organizing controls

By using the group type we can create a group of controls that help create a mental model for the seller to manipulate the hero image in this section. These include controls that change image source, the image alt tag, and where the image links to.

While these controls can exist individually, combining and grouping primitives together help create a more intuitive experience for sellers.

Group toggle

A variation of the group control is the group-toggle control that creates a group of controls that can be toggled on and off. A common use case would be a group of controls that are tied to an element that is conditionally rendered. For example, the previous example could conditionally render the hero image:

{% editor %}
{
  "name": "Hero section",
  "controls": [
    {
      "control": "input",
      "label": "Title",
      "value": {
	"$ref": "#/schema/title"
      }
    },
    {
      "control": "group-toggle",
      "label": "Hero image",
      "value": {
        "$ref": "#/schema/showHero"
      },
      "children": [
        {
	  "control": "input",
	  "label": "Alt",
	  "value": {
	    "$ref": "#/schema/hero/alt"
	  }
	},
	{
	  "control": "input",
	  "label": "Image description",
	  "value": {
	    "$ref": "#/schema/hero/description"
	  }
	}
      ]
    }
  ]
}
{% endeditor %}

Organizing controls

Using twig to generate controls config

Twig is supported within editor tags. You can use it to do all of the usual things you’d do in a template. Some examples described below.

Variable interpolation

Let’s say you have a theme/config/placeholder.json file where you maintain the default placeholders for a bunch of different controls, which looks something like this:

{
  "input": "input placeholder"
}

You can reference that in your editor tag like so:

{% editor %}
{
  "controls": [
    {
       "control": "input",
       "placeholder": "{{ config.placeholders.input }}"
    }
  ]
}
{% endeditor %}

Conditional rendering

Although the group-toggle control exists, it could be re-implemented using twig, here’s an example:

{% editor %}
{
   "controls": [
      {
        "control": "toggle",
        "label": "Font controls",
        "value": { "$ref": "#/schema/showFontControls" }
      },
     {% if showFontControls %}
       {
          "control": "input",
          "label": "Heading font",
          "value": { "$ref": "#/schema/headingFont" }
       },
       {
          "control": "input",
          "label": "Body font",
          "value": { "$ref": "#/schema/bodyFont" }
       }
     {% endif %}
   ]
}
{% endeditor %}

Functions

All twig functions are available, so you could for example use localize if you wanted to localize control labels:

{% editor %}
{
   "controls": [
      {
        "control": "image-chooser",
        "label": "{{ 'controls.image.label' | localize }}",
        "value": { "$ref": "#/schema/image" }
      }
   ]
}
{% endeditor %}

See also