Building Shopify Section Schemas with JavaScript

The Liquid Schema Plugin allows you to export your Shopify Section schemas from JS and JSON files, in turn allowing you to build your schemas dynamically, share schema partials between several schemas, and benefit from the language support provided by IDEs such as VSCode.

In order to use the plugin, the build system you’re using must be Webpack based, and you must be able to add new plugins to your Webpack setup. If you just want to set the plugin up quickly, simple installation instructions can be found in the plugin README.

Outlined below are some of the use cases I’ve found the plugin to be really handy for:

Shared Section Schema

It’s common for landing pages across a theme to use one or more sections, however since (for the most part) sections can’t be reused with different settings, this requires section files to be duplicated for use on new landing pages. Unfortunately, it can become difficult to maintain multiple of the same schema in this way, because if one needs to be updated, you must remember to update each copy of the schema. Over time this can lead to multiple section schemas, each with slight differences.

As an example, take the following use case: a landing page needs to be created for a new “Spring Summer 2021” range. The page requires that the same components are available as on an existing “Autumn Winter 2020” page. In order to ensure the section schema remain in sync here, you’d first move the schema from the Autumn Winter 2020 section file to its own schema file. Let’s say seasonal-landing.js for now. It would look something like this:

module.exports = {
  name: 'Seasonal Landing',
  settings: [
    // section settings…
  ],
  blocks: [
    // section blocks…
  ]
}

From here, you would replace the schema tag in your existing liquid file with this:

{%  schema 'seasonal-landing' %}

Now the plugin will inject the object exported by seasonal-landing.js into your section file. All you need to do is duplicate the section file and rename it for the Spring Summer 2021 section. If you ever need to update one of the schemas, all sections with the above schema tag will get the same updates with no extra work.

Using a section as a section block

Occasionally you might build a component as a section, which needs to be repurposed as a section block elsewhere. To do this, move the settings array into its own JS file, and import it back into the original schema. The settings can then be imported into another section too.

As an example, let’s say we have a hero banner section that needs to be added as a block for one of our landing pages. First, we move the hero banner settings into their own file, then import them into the original hero banner schema:

// partials/hero-banner.js
module.exports = [
  // section settings…
]
// hero-banner.js
const settings = require('./partials/hero-banner.js')

module.exports = {
  name: 'Hero Banner',
  settings
}

We’d then add the same settings to our new landing page schema like so:

// landing-page.js
const heroBannerSettings = require('./partials/hero-banner.js')

module.exports = {
  name: 'Landing Page',
  blocks: [
    {
      name: 'Hero Banner',
      type: 'Hero Banner',
      settings: heroBannerSettings
    }
  ]
}

Common Fieldsets

I often find myself repeating the same fields within several schemas across a theme. Take for example a link. Shopify doesn’t have a fieldtype with both a text input and URL input yet (it looks like it is coming though), so for every section where we need to add a link, we need to add an input for customising the link text and an input for setting its URL. This can be achieved like so:

// partials/link.js
module.exports = [
  {
    label: 'Link Text',
    id: 'link_text',
    type: 'text'
  },
  {
    label: 'Link URL',
    id: 'link_url',
    type: 'url'
  }
]
// hero-banner.js
const linkSettings = require('./partials/link')

module.exports = {
  name: 'Hero Banner',
  settings: [
    {
      label: 'Title',
      id: 'title',
      type: 'text'
    },
    …linkSettings
  ]
}

Now throughout all our schemas, we can add the same fields for any and all links. If we ever want to add another option, for example to add a selector for the link style, or the link colour; it can be added to this partial and every schema that uses this partial will get those extra options.

Looping Fieldsets

Consider the previous example, but imagine the hero banner needs to support 2 links as opposed to 1. First of all we’d change the link partial to a function where we can specify the number of links we need to have available.

// partials/link.js
const createLinks = (total = 1) => {
  return new Array(total).fill(null).flatMap((_, index) => {
    const currentIteration = index + 1
    return [
      {
        label: `Link Text ${currentIteration}`,
        id: `link_text_${currentIteration}`,
        type: 'text'
      },
      {
        label: `Link URL ${currentIteration}`,
        id: `link_url_${currentIteration}`,
        type: 'url'
      }
    ]
  })
}

Now we change the hero banner schema:

// hero-banner.js
const createLinks = require('./partials/link')

module.exports = {
  name: 'Hero Banner',
  settings: [
    {
      label: 'Title',
      id: 'title',
      type: 'text'
    },
    …createLinks(2)
  ]
}

Adding section-specific schema

The plugin will run functions exported from injected JS modules. These functions are passed the filename and schema tag content as parameters. This means if you need to make any section specific overrides, you can do so in the section file itself. This is commonly used to name specific sections. For example if you have multiple landing pages with the same schema, you might want to give each their own name. To do this you might do the following:

// autumn-winter-2020.liquid
{% schema 'landing-page' %}
{
  "name": "Autumn Winter 2020"
}
{% endschema %}
// spring-summer-2021.liquid
{% schema 'landing-page' %}
{
  "name": "Spring Summer 2021"
}
{% endschema %}
// landing-page.js
module.exports = (filename, content) => {
  name: content.name,
  // settings…
}

Note that you could also use the filename for this. The JSON contained within the schema tag is generally easier to use though.

What other benefits does the plugin provide?

The plugin isn’t only useful for sharing schema across multiple sections and building them dynamically. Since Shopify development is a relatively niche area, there’s a lack of tooling support for really making schemas easy to write and edit. Fortunately, the content of a schema tag is just JSON. By moving the schemas to JSON and JS files, we can benefit from much greater language support within our code editors.

If you’ve ever had to edit a particularly long schema, you might have found that it’s really difficult to keep track of where you are. Even if the indentation is perfect, the number of brackets required to represent arrays and objects can make a long JSON file difficult to navigate. In section schema specifically, I have often found it difficult to keep track of which block type I’m editing the settings array for. Fortunately, JSON and JS files can take advantage of code folding, which allows us to hide large quantities of code so that we can more easily visualise the schema structure. If that’s too difficult, VSCode (and I’d assume other editors too) can provide a breadcrumb style breakdown of exactly where in the object your cursor sits, which can make navigating a large schema really easy.

Finally, it’s often bugged me in the past just how easy it is to make a mistake within a schema. Be it a trailing comma at the end of an array, or simply forgetting to wrap a key in quotes. Using actual JSON or JS files for the schemas lets us forget about this issue for the most part. Since our editor knows we’re trying to write valid JSON, or JS, it will warn us where we’ve made a mistake.

It all boils down to time and effort

Detailed above are a number of practical ways in which section schemas can be built dynamically. Ultimately I find each of these saves me time and effort when building Shopify themes. Be that in the immediate term, or a few months down the line.

Section schemas are typically presented to us as being the same concern as the section itself. By creating them outside of your section files, it becomes easier to understand them simply as a group of fields that can be generated by a program. You might build a function capable of outputting a huge variety of different schemas, or you might simply use it to keep 2 or 3 section schemas in sync. In my experience, I have yet to work on a Shopify theme where building my section schemas in JS had no benefit at all.

If you would like to use the Liquid Schema Plugin on your project, please check the GitHub repo.