Skip to content
All projects
CMS2023

A Shopify storefront the merchandising team could run

Rebuilding a bloated theme around a constrained set of sections — so campaigns ship without a developer, and the storefront stops being slow because of apps nobody uses.

Role
Theme & frontend lead · DTC commerce client
Stack
ShopifyLiquidJavaScriptSection schemasCore Web Vitals
  • Merchandising composes and ships pages without a deploy
  • Section schemas make an unusable layout impossible to build
  • Checkout and commerce primitives left to Shopify, deliberately

The problem

Two complaints, which turned out to be the same complaint.

The merchandising team could not change the homepage without filing a ticket, so campaigns ran late and were rarely adjusted once live. And the storefront was slow — slow enough to be visible in the analytics, on mobile especially.

The proposal on the table when I arrived was to go headless. That would have solved neither problem and created several: headless would have taken the theme editor away from the exact team who needed more control, and the performance issue was not in the theme at all.

Finding where the weight actually was

Before touching anything, I profiled a cold mobile load. The theme's own CSS and JavaScript were a minority of the blocking work. The majority was app scripts — each installed for a reason, each adding tags to every page including ones where the app did nothing.

That reframed the project. The performance work was mostly deletion, and the real build was giving merchandising control.

Sections as a content model

Shopify's section schema is a content model with a visual editor attached, and it is better than its reputation — provided you constrain it.

{% schema %}
{
  "name": "Featured collection",
  "settings": [
    { "type": "collection", "id": "collection", "label": "Collection" },
    {
      "type": "range",
      "id": "product_count",
      "label": "Products to show",
      "min": 2, "max": 12, "step": 2, "default": 4
    },
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "info": "Around 40 characters — longer wraps to three lines on mobile."
    }
  ],
  "presets": [{ "name": "Featured collection" }]
}
{% endschema %}

Two details there carry most of the value.

The range with a step of two means a merchant cannot pick a product count that breaks the grid — the option simply isn't offered. Constraints belong in the schema, not in a document nobody reads.

The info string explains why. A merchant who hits a limit with no explanation assumes the system is being arbitrary and asks an engineer to remove it. One who knows it exists because of small screens works within it.

What we deliberately did not build

Checkout stayed Shopify's. So did cart state, discount handling, gift cards, market and currency switching, and tax display. All of it is fiddly, all of it is already correct, and rebuilding any of it would have moved PCI scope and edge cases onto us in exchange for nothing a customer would notice.

The same reasoning kept the remaining apps on theme extensions rather than reimplementing what they did.

Outcome

Merchandising composes homepage and landing pages from a fixed set of sections and ships campaigns without a deploy. The storefront is materially faster on mobile, mostly by having removed scripts rather than by having written better ones.

The decision I'd defend hardest is the one not to go headless. The team's problem was never Liquid — it was that they had no compositional control and four apps too many. Headless would have taken away the editor that fixed the first problem, and done nothing about the second.