Shopify Development

Common Mistakes in Shopify Custom Theme Development (And How to Avoid Them)

Part 1 of an 8-part series on building production-grade Shopify themes   A custom Shopify theme is supposed to be a competitive advantage — faster pages, a design that actually…

AKHTAR LABS·Aug 23, 2026·1 min read·3 views
Common Mistakes in Shopify Custom Theme Development (And How to Avoid Them)
article-preview

Part 1 of an 8-part series on building production-grade Shopify themes   A custom Shopify theme is supposed to be a competitive advantage — faster pages, a design that actually…

Quick answer

Treat Shopify's sections and blocks model as the foundation of the theme, not an afterthought bolted on later.

Performance problems are almost always caused by render-blocking scripts, unoptimized images, and app bloat — not by Liquid itself.

Hardcoding content into .liquid files instead of exposing it through schema settings creates a theme merchants can't actually manage.

Skipping responsive and cross-device testing until the end guarantees a rebuild, not a fix.

Accessibility is not optional polish — it affects real customers and, increasingly, compliance requirements.

Online Store 2.0's JSON templates are frequently misunderstood, leading developers to fight the platform instead of using it.

01

1. Treating the Theme Like a Static Website Instead of a Liquid System

The most common root cause of a fragile Shopify theme is a mental model mismatch: developers coming from static HTML/CSS or WordPress backgrounds often build a Shopify theme the way they'd build a normal website — one big template per page, content typed directly into the markup. Shopify doesn't work that way, and fighting the platform's architecture is the single biggest source of technical debt in custom themes.

What goes wrong in practice:
sections/hero.liquid
<!-- sections/hero.liquid — the "just get it done" version -->

<div class="hero">
  <h1>Summer Sale — Up to 50% Off</h1>
  <p>Shop our biggest collection drop of the year.</p>
  <a href="/collections/summer-sale" class="btn">Shop Now</a>
</div>

This renders fine. It also means every single copy change — a new headline, a different button link, a seasonal campaign — requires a developer to edit code and redeploy. Six months later, the client is emailing you to change one sentence.

What a properly structured section looks like:
sections/hero.liquid
<div class="hero">
  <h1>{{ section.settings.heading }}</h1>
  {% if section.settings.subheading != blank %}
    <p>{{ section.settings.subheading }}</p>
  {% endif %}
  {% if section.settings.button_label != blank %}
    <a href="{{ section.settings.button_link }}" class="btn">
      {{ section.settings.button_label }}
    </a>
  {% endif %}
</div>
{% schema %}

{

  "name": "Hero banner",

  "settings": [
    { "type": "text", "id": "heading", "label": "Heading", "default": "Summer Sale — Up to 50% Off" },
    { "type": "text", "id": "subheading", "label": "Subheading" },
    { "type": "text", "id": "button_label", "label": "Button label", "default": "Shop Now" },
    { "type": "url", "id": "button_link", "label": "Button link" }
  ],

  "presets": [
    { "name": "Hero banner" }
  ]
}

{% endschema %}

Same visual result, but now the merchant edits everything from the theme customizer — no code, no deploy, no developer ticket for a headline change. This single habit change is responsible for more client satisfaction than almost anything else in this list.

shopify development | akhtar labs
shopify development | akhtar labs
02

2. Ignoring Performance Until It's a Problem

Shopify themes get judged on Core Web Vitals whether the merchant asks for it or not — it affects SEO ranking, mobile conversion rate, and, since 2023, is directly scored in the Online Store Speed Report inside the Shopify admin. Yet performance is almost always the last thing addressed in a build, if it's addressed at all.

The usual culprits are predictable:

  • Unoptimized images. Full-resolution product photography served at hero-banner size, with no srcset, no lazy loading, and no explicit width/height (causing layout shift).
  • Render-blocking JavaScript. Every third-party app — reviews, upsells, chat widgets — adds its own script tag to theme.liquid, often synchronously, often in <head>.
  • Unused CSS shipped on every page. A single global stylesheet loaded on the cart page that also contains styles for the blog, the FAQ accordion, and three sections nobody uses anymore.
  • Too many Liquid loops re-computing the same data. Looping over collection.products three times in three different sections instead of computing once and reusing.
A responsive image snippet is a small fix with an outsized impact:
snippets/responsive-image.liquid
{% assign img = product.featured_media.preview_image %}
<img
  src="{{ img | image_url: width: 800 }}"
  srcset="
    {{ img | image_url: width: 400 }} 400w,
    {{ img | image_url: width: 800 }} 800w,
    {{ img | image_url: width: 1200 }} 1200w
  "
  sizes="(min-width: 990px) 50vw, 100vw"
  width="{{ img.width }}"
  height="{{ img.height }}"
  loading="lazy"
  alt="{{ img.alt | escape }}"
>
This alone — correct srcset, explicit dimensions, and loading="lazy" — typically resolves the two most common Core Web Vitals failures: Largest Contentful Paint and Cumulative Layout Shift.
shopify development | akhtar labs
shopify development | akhtar labs
03

3. Hardcoding Content That Should Be Merchant-Editable

This deserves its own section separate from mistake #1, because it extends beyond sections into theme settings, metafields, and blocks.   A recurring pattern: a developer builds a "Featured Collections" section with exactly three hardcoded collection blocks, styled and positioned by hand. It looks perfect on launch day. Two weeks later, the merchant wants four collections, or wants to reorder them, or wants to swap one out for a seasonal promotion — and none of that is possible without editing code.   The fix is to expose repeatable content as blocks, not fixed markup:
{
  "name": "Featured collections",
  "max_blocks": 6,
  "blocks": [
    {
      "type": "collection",
      "name": "Collection",
      "settings": [
        { "type": "collection", "id": "collection", "label": "Collection" },
        { "type": "text", "id": "custom_title", "label": "Override title (optional)" }
      ]
    }
  ],
  "presets": [{ "name": "Featured collections" }]
}
With this structure, the merchant adds, removes, and reorders collection blocks directly in the customizer — the exact kind of flexibility that separates a "custom theme" from "a static page a developer built once."   For content that isn't collection- or product-specific — a shipping policy blurb, a size-guide table, a set of trust badges — metafields are usually the better tool than section settings, since they attach structured data directly to the resource (product, page, or shop) rather than to a single section instance.
04

4. Skipping Real Device Testing Until the End

Responsive design in Shopify themes tends to get tested exactly twice: once in the browser's device-emulation panel at 375px and 1440px, and once — briefly — on whatever phone happens to be on the desk. That is not the same as testing on a real device grid.   Where this bites teams most often:  
  • Tablet breakpoints (768px–1024px) are the most neglected range. Layouts that work at phone width and desktop width frequently break awkwardly in between — two-column grids that should be one, or three-column grids that should be two.
  • Touch targets. A dropdown menu that works perfectly with a mouse hover often has no equivalent tap behavior on touch devices, quietly breaking navigation for the majority of Shopify's traffic, which is mobile.
  • Sticky elements and safe areas. Sticky "Add to Cart" bars that ignore env(safe-area-inset-bottom) end up hidden behind the home-indicator bar on iOS devices.
  • Font scaling. Fixed px font sizes that don't respond to a user's OS-level text-size settings, which is both a UX gap and an accessibility gap (see next section).
  A practical minimum bar: test on an actual iPhone, an actual mid-range Android device, and an actual iPad — not just simulators — before calling a section "done." It takes fifteen minutes and catches issues that browser DevTools genuinely cannot reproduce (real touch latency, real font rendering, real network conditions).
shopify development | akhtar labs
shopify development | akhtar labs
05

5. Treating Accessibility as Optional Polish

Accessibility gets cut from Shopify theme projects more often than almost any other requirement, usually because it's invisible if you're not the one relying on it. It shouldn't be optional — a meaningful share of any store's visitors use screen readers, keyboard navigation, or have visual impairments that basic semantic HTML and ARIA support account for at near-zero extra cost.   Common, cheap-to-fix gaps we see repeatedly:
<!-- Common mistake: icon-only button with no accessible name -->
<button class="cart-icon">
  {% render 'icon-cart' %}
</button>
<!-- Fixed: -->
<button class="cart-icon" aria-label="Open cart, {{ cart.item_count }} items">
  {% render 'icon-cart' %}
</button>
Other frequent issues worth checking on every theme build:  
  • Color contrast ratios below WCAG AA (4.5:1 for body text) on brand-colored buttons and badges.
  • Modals and drawers (cart, search, mobile nav) that don't trap focus or return focus to the triggering element on close.
  • Missing alt text on product images — especially damaging on an e-commerce site, since product images often are the content.
  • Carousels that autoplay with no pause control, which is a WCAG failure and also, frankly, an annoying UX pattern.
  None of this requires an accessibility specialist to get right; it requires making it part of the definition of "done" for a section, rather than a pass done at the very end (or never).
06

6. Misunderstanding Online Store 2.0's JSON Template Architecture

Since Online Store 2.0 shipped, Shopify themes use JSON templates (templates/product.json, templates/index.json, etc.) that define which sections appear on a page and in what order — as opposed to the old .liquid templates that hardcoded that structure. A lot of developers who learned Shopify before this shift (or learned from outdated tutorials) still build themes the old way: one monolithic product.liquid file with everything hand-coded inline.   The result is a theme that looks modern but doesn't behave like one — merchants can't rearrange sections on the product page, can't add an extra content block between "Description" and "Reviews," and lose most of the flexibility Online Store 2.0 was built to provide.
templates/product.json
{
  "sections": {
    "main": {
      "type": "main-product",
      "settings": {}
    },
    "description": {
      "type": "product-description",
      "settings": {}
    },
    "reviews": {
      "type": "product-reviews",
      "settings": {}
    }
  },
  "order": ["main", "description", "reviews"]
}
Building product, collection, and page templates this way — as an ordered list of independent, reusable sections — is what actually unlocks drag-and-drop rearranging in the customizer. It's more setup work upfront than a single hardcoded file, and it is the difference between a theme merchants can grow into and one they immediately outgrow.
07

Tools & Technologies Referenced

  • Shopify Liquid
  • Online Store 2.0 JSON templates
  • Shopify Theme Customizer
  • Metafields
  • Lighthouse / Core Web Vitals
  • WCAG 2.1 AA
08

Conclusion

None of these mistakes come from a lack of Shopify knowledge — they come from treating a Shopify theme like a regular website project instead of respecting the platform's own architecture: sections, blocks, metafields, and JSON templates. Every one of them is fixable, and fixable early is dramatically cheaper than fixable after launch, after the client's first content request, or after the first accessibility complaint.   The next post in this series goes deep on Shopify theme performance specifically — the exact profiling workflow, the app-bloat audit process, and the Liquid patterns that actually move Core Web Vitals scores, with real before/after numbers from a production store.
shopify development | akhtar labs
shopify development | akhtar labs
Coming up in this series:
  1. Common Mistakes in Shopify Custom Theme Development (this post)
  2. Shopify Theme Performance: A Real Optimization Workflow
  3. Mastering Sections, Blocks, and the Theme Customizer
  4. Metafields and Metaobjects for Structured Content
  5. Building Accessible Shopify Themes from the Ground Up
  6. Liquid Patterns Every Theme Developer Should Know
  7. Testing and QA Workflows for Shopify Themes
  8. Migrating a Legacy Theme to Online Store 2.0

Have a Shopify storefront that feels slower or harder to manage than it should? We build and audit custom Shopify themes end to end — get in touch and we'll take a look.

Part 1 of an 8-part series on building production-grade Shopify themes

 

A custom Shopify theme is supposed to be a competitive advantage — faster pages, a design that actually matches the brand, and a storefront that converts better than a stock theme ever could. In practice, a lot of custom themes end up slower, harder to maintain, and more fragile than the free themes they replaced.

 

That gap almost never comes from a lack of skill. It comes from a handful of decisions made early in the build — decisions that feel harmless in week one and become expensive by month three. This first article in the series walks through the mistakes we see most often in real Shopify theme projects, why they happen, and what to do instead. Later posts in this series will go deep on performance, Online Store 2.0 architecture, accessibility, and Liquid patterns — this one sets the foundation.