Investigating Shopify’s new Slate Schema Plugin

I’ve been keeping a close eye on the development of Slate over the past few months, and a recently approved pull request caught my attention. The new Slate Sections Plugin improves section management by separating schema and translations from the liquid templates.

So how does it work?

Fortunately, this is a relatively simple plugin in concept. Let’s take a look at a sample section as it might currently appear in our templates:

<section>
  <h1>{{ section.settings.title }}</h1>

  <div class="carousel">
    {%- for slide in section.blocks -%}
      {%-
        include 'carousel-slide',
        content: slide.content,
        cta_text: slide.cta_text,
        cta_url: slide.cta_url,
      -%}
    {%- endfor -%}
  </div>
</section>

{%- schema -%}
{
  "name": "Carousel",
  "settings": [
    {
      "label": "Title",
      "id": "title",
      "type": "text"
    }
  ],
  "blocks": [
    {
      "name": "Slide",
      "type": "slide",
      "settings": [
        {
          "label": "Content",
          "id": "content",
          "type": "richtext"
        },
        {
          "label": "CTA URL",
          "id": "cta_url",
          "type": "url"
        },
        {
          "label": "CTA Text",
          "id": "cta_text",
          "type": "text"
        }
      ]
    }
  ]
}
{%- endschema -%}

This section is already 50 lines long, and it’s quite bare. As we start adding more settings and block types it’s quickly going to expand. If you’re developing a theme to sell on the marketplace, each of your schema settings are likely being given multiple translations too. This can all become difficult to manage quite quickly, so the new plugin splits these parts up.

In the example above, our carousel.liquid file would become a carousel folder, containing a template.liquid for, you guessed it, the template. The schema is written in a schema.json and translations are included per-language in a locales sub-folder. The structure of this new section would look like this:

└── carousel/
   ├── locales/
   ├── en.json
   ├── es.json
   └── fr.json
   ├── schema.json
   └── template.liquid

The plugin then combines the contents of these files into a carousel.liquid in dist/sections/.

Is it magic?

The plugin itself as actually relatively simple. If you think about it, the schema in a section is just a JSON object contained within {% schema %} tags. Currently merging the files is essentially a two step process. The steps required are:

  1. Merge translations into schema
  2. Merge schema into template

In order to get the translations, the plugin loops through translatable section keys such as label. If any are an object with a t property, the plugin will determine the location of the translations based on the value of t. For example, if the Title setting in my example was translatable, it might look like this:

{
  "label": {
    "t": "settings.title"
  },
  "id": "title",
  "type": "text"
}

In the example above, having read settings.title, the plugin will then know that it needs to get the following from each locale file:

{
  "settings": {
    "title": "Title"
  }
}

So we end up with:

{
  "label": {
    "en": "Title"
  },
  "id": "title",
  "type": "text"
}

Once the plugin has finished merging translations into the schema, it’s simply a case of appending the JSON object to the end of template.liquid by placing it inside schema tags like so: {% schema %} { ... } {% endschema %} .

Where do we go from here?

Despite simply being much cleaner, this is going to make it much easier to make more complex and dynamic sections.

For starters, it is likely that we’ll see section specific JS and stylesheets come in the near future. In a similar manner to how it currently works, I suspect that the plugin may only need to extract text from a scripts.js file and insert it into the template between {% javascript %} tags, and similarly with CSS or Sass, extract the text and place it between {% stylesheet %} tags within the template. Eventually we may even have the ability to use ES6 imports per section and this plugin might make it easier for Webpack to run its code splitting magic per section too.

Another thing that is now easier for us to do is generate the section schema programatically. Imagine that, for example, you have specific user groups on your store, and you want to show each of them different offers. To start off with, the different users are each tagged with user_group:[GROUP]. Now, with an API call, you could retrieve a list of all tags beginning with user_group: and run a script to update a section schema with blocks for every available group. Alternatively you might be building a landing page on which your client needs to be able to list a large number of products from their range. These products aren’t tied to any specific collection, and you don’t want to create loads of separate options, which only differ because they each have a different number associated with their IDs. Instead, you could simply use JS to loop those settings however many times you need, and output them into your schema.json.

How about partials? Saving the best until last, partials are now easier than ever before to integrate into your schema. You could create a new schema-partials directory, containing a JSON files which are shared across multiple sections. Then, run a script to replace instances of ”partial”: “x” in your schema.json with the JSON from schema x.

What can we expect to come?

Unfortunately Slate is now a “Low Maintenance” project, so we probably can’t expect much more active development on it for now. However; Slate v1 serves as a great platform on which tools to aid theme development can be built. We are able to hook into the Webpack config used by Slate and so can develop our own plugins for it, giving us greater control over the tools we have at our disposal.