Variant-Aware Content

Text and images that change when the shopper picks a different colour or size.

One section file Product page, below the product information Edit in the theme editor

Important: Before making any changes to your theme, duplicate it in Online Store > Themes so you have a backup.

Different variants often need different words: care instructions for each material, fit notes for each size, or styling ideas for each colour. This section shows a block of content for the selected variant and switches it as soon as the shopper picks another.

Preview

What you'll build

Colour: Red Navy Olive

Why choose red

Garment-dyed for a soft, lived-in colour that fades beautifully over time.

Preview with the default settings. Colours, text and layout can all be changed in the theme editor.

Install

Add it to your store in 4 steps

  1. 1

    Open your theme code

    In Shopify admin go to Online Store > Themes. Next to the theme you want to edit, click the three dots and choose Edit code.

  2. 2

    Create the section

    In the Sections folder click Add a new section, name it lt-variant-aware-content, delete the placeholder code, paste in the code below and click Save.

  3. 3

    Add it in the theme editor

    Click Customize, go to Products > Default product, click Add section and choose Variant content.

  4. 4

    Make it yours

    Change the settings in the sidebar, drag the section where you want it, and click Save.

sections/lt-variant-aware-content.liquid · 195 lines
{%- comment -%}
  Variant-aware content by LAUNCHTIP
  https://launchtip.com/free-shopify-sections-code
  Each block is shown when the selected variant matches its "Show for" value.
  Match a single option value (Red, Large) or a full variant title (Large / Red).
{%- endcomment -%}
{%- if product != blank -%}
  {%- liquid
    assign current = product.selected_or_first_available_variant
    assign matched_any = false
    for block in section.blocks
      if block.type == 'content'
        if block.settings.match == current.title or current.options contains block.settings.match
          assign matched_any = true
        endif
      endif
    endfor
  -%}

  {%- style -%}
    #lt-{{ section.id }} { padding: {{ section.settings.padding }}px 0; }
    #lt-{{ section.id }} [data-match][hidden] { display: none; }
    #lt-{{ section.id }} .lt-vac__block { display: grid; gap: 20px; align-items: center; }
    @media (min-width: 750px) { #lt-{{ section.id }} .lt-vac__block--image { grid-template-columns: 1fr 1fr; } }
    #lt-{{ section.id }} .lt-vac__block img { width: 100%; height: auto; border-radius: 10px; display: block; }
    #lt-{{ section.id }} .lt-vac__block h3 { margin: 0 0 8px; }
  {%- endstyle -%}

  <div id="lt-{{ section.id }}" class="page-width">
    {%- for block in section.blocks -%}
      {%- liquid
        assign visible = false
        if block.type == 'content'
          if block.settings.match == current.title or current.options contains block.settings.match
            assign visible = true
          endif
          assign match = block.settings.match
        else
          assign match = '*'
          unless matched_any
            assign visible = true
          endunless
        endif
      -%}
      <div
        class="lt-vac__block{% if block.settings.image != blank %} lt-vac__block--image{% endif %}"
        data-match="{{ match | escape }}"
        {% unless visible %}hidden{% endunless %}
        {{ block.shopify_attributes }}
      >
        {%- if block.settings.image != blank -%}
          {{ block.settings.image | image_url: width: 900 | image_tag: loading: 'lazy', widths: '450, 900', sizes: '(min-width: 750px) 50vw, 100vw' }}
        {%- endif -%}
        <div>
          {%- if block.settings.heading != blank -%}<h3>{{ block.settings.heading }}</h3>{%- endif -%}
          {{ block.settings.text }}
        </div>
      </div>
    {%- endfor -%}

    <script type="application/json" data-variants>
      { {%- for v in product.variants -%}"{{ v.id }}": { "title": {{ v.title | json }}, "options": {{ v.options | json }} }{%- unless forloop.last -%},{%- endunless -%}{%- endfor -%} }
    </script>
  </div>

  <script>
    (function () {
      var root = document.getElementById('lt-{{ section.id }}');
      if (!root || root.dataset.ready) return;
      root.dataset.ready = '1';

      var variants = JSON.parse(root.querySelector('[data-variants]').textContent);
      var blocks = Array.prototype.slice.call(root.querySelectorAll('[data-match]'));
      var form = document.querySelector('main form[action*="/cart/add"]') || document.querySelector('form[action*="/cart/add"]');
      var lastId = null;

      function norm(text) { return String(text || '').trim().toLowerCase(); }

      function currentId() {
        var input = form && form.querySelector('[name="id"]');
        if (input && input.value) return input.value;
        return new URLSearchParams(window.location.search).get('variant');
      }

      function update() {
        var id = currentId();
        if (!id || id === lastId || !variants[id]) return;
        lastId = id;
        var variant = variants[id];
        var values = [norm(variant.title)].concat(variant.options.map(norm));
        var matchedAny = false;
        blocks.forEach(function (block) {
          if (block.dataset.match === '*') return;
          var show = values.indexOf(norm(block.dataset.match)) > -1;
          block.hidden = !show;
          if (show) matchedAny = true;
        });
        blocks.forEach(function (block) {
          if (block.dataset.match === '*') block.hidden = matchedAny;
        });
      }

      document.addEventListener('change', update);
      setInterval(update, 400);
      update();
    })();
  </script>
{%- endif -%}

{% schema %}
{
  "name": "Variant content",
  "tag": "section",
  "class": "lt-vac-section",
  "enabled_on": {
    "templates": ["product"]
  },
  "settings": [
    {
      "type": "range",
      "id": "padding",
      "label": "Vertical padding",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "default": 24
    }
  ],
  "blocks": [
    {
      "type": "content",
      "name": "Variant content",
      "settings": [
        {
          "type": "text",
          "id": "match",
          "label": "Show for",
          "default": "Red",
          "info": "An option value such as Red or Large, or a full variant title such as Large / Red. Not case-sensitive."
        },
        {
          "type": "text",
          "id": "heading",
          "label": "Heading",
          "default": "Why choose red"
        },
        {
          "type": "richtext",
          "id": "text",
          "label": "Text",
          "default": "<p>Describe what's different about this variant: the material, the fit, care instructions or what it pairs with.</p>"
        },
        {
          "type": "image_picker",
          "id": "image",
          "label": "Image"
        }
      ]
    },
    {
      "type": "fallback",
      "name": "Fallback content",
      "limit": 1,
      "settings": [
        {
          "type": "text",
          "id": "heading",
          "label": "Heading"
        },
        {
          "type": "richtext",
          "id": "text",
          "label": "Text",
          "default": "<p>Shown when no other block matches the selected variant.</p>"
        },
        {
          "type": "image_picker",
          "id": "image",
          "label": "Image"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Variant content",
      "blocks": [
        { "type": "content" },
        { "type": "fallback" }
      ]
    }
  ]
}
{% endschema %}
Customization

What you can change in the theme editor

Setting Default
Vertical padding 24px

Blocks

Add, remove and reorder these inside the section:

  • Variant content : show for, heading, text, and image
  • Fallback content : heading, text, and image
Good to know

Tips for setting it up

  • Match a single option value such as Red or Large, or a full variant title such as Large / Red. Matching ignores capital letters.
  • Add a Fallback content block to show when nothing matches.
  • Several blocks can match at once, for example one for the colour and one for the size.
FAQ

Frequently asked questions

Does it change without reloading the page?

Yes. It watches the variant selected in the product form and switches content straight away.

Can I use metafields instead?

Yes. Variant metafields are another way to do this. This section is quicker to set up when only a few variants need extra content.

What if my theme changes variants differently?

The section checks the product form and the variant in the URL, which covers most themes. Test it on yours before publishing.

More free sections

New

Recently Viewed Products

Shows each shopper the products they looked at, so they can get back to them in a tap.

New

Sticky Add-to-Cart Bar

A bar that slides up when the buy button scrolls off screen, with price and variant.

New

Blog Posts Grid for Homepage

Your latest blog posts in a clean grid with images, dates and excerpts.

Explore More Free Sections

Discover more code snippets to enhance your Shopify store.

View All Sections
SellUp Increase AOV with Upsells