Skip to main content
Tutorial

How to Use HTML2Liquid: From HTML to a Tested Shopify Section

Convert a complete HTML hero into an editable Shopify section, inspect the generated Liquid and schema, then test the result safely in a development theme.

4 min read
TutorialLiquidShopifySchema

HTML2Liquid turns a static fragment into a Shopify section file: HTML and Liquid at the top, followed by one valid JSON object inside {% schema %}. The converter is most useful as a starting point for theme work, not as a substitute for reviewing markup in a development theme.

This walkthrough uses a small hero because it contains the decisions that appear in real projects: which text should be editable, how a link differs from its label, how an image picker is rendered, and where rich text belongs.

Start with semantic, self-contained HTML

Use a fragment that has one clear responsibility. Remove document-level tags such as <html>, <head>, and <body>, along with scripts and inline event handlers. HTML2Liquid rejects scripts instead of copying executable input into a theme.

Paste this into the HTML converter:

<section class="promo-hero">
  <div class="promo-hero__content">
    <h2>Build a better storefront</h2>
    <p>Give merchants control over the copy, destination, and image.</p>
    <a class="promo-hero__button" href="/collections/all">Shop the collection</a>
  </div>
  <img
    class="promo-hero__image"
    src="hero-placeholder.jpg"
    alt="Products arranged on a display table"
    width="1200"
    height="800"
  >
</section>

The source href, src, and text become editor settings. Classes remain in the output so existing CSS can still target the same elements.

Choose the conversion mode

Manual conversion is immediate, does not require an account, and works well for headings, paragraphs, images, links, buttons, videos, lists, and repeated card-like markup. AI-assisted conversion is optional and requires a free sign-in because it uses a shared external API with a universal fair-use allowance.

Start with manual mode. It is deterministic, making output changes easier to review. Use AI-assisted mode only when a layout has ambiguous repeated structures or needs more semantic interpretation, and review its output with the same care as handwritten code.

Understand the generated Liquid

A production section based on the input should follow this shape:

<section class="promo-hero">
  <div class="promo-hero__content">
    {% if section.settings.heading != blank %}
      <h2>{{ section.settings.heading | escape }}</h2>
    {% endif %}

    {% if section.settings.body != blank %}
      <div class="rte">{{ section.settings.body }}</div>
    {% endif %}

    {% if section.settings.button_text != blank and section.settings.button_url != blank %}
      <a class="promo-hero__button" href="{{ section.settings.button_url }}">
        {{ section.settings.button_text | escape }}
      </a>
    {% endif %}
  </div>

  {% if section.settings.image != blank %}
    {{
      section.settings.image
      | image_url: width: 1600
      | image_tag:
        class: 'promo-hero__image',
        loading: 'lazy',
        widths: '480, 750, 1100, 1600',
        sizes: '(min-width: 990px) 50vw, 100vw',
        alt: section.settings.image.alt
    }}
  {% endif %}
</section>

Notice three safety details:

  • Plain text uses the escape filter.
  • A richtext value is not wrapped in another <p> because Shopify already returns paragraph markup.
  • The selected image's merchant-authored alt text is passed to image_tag.

Use valid section schema

The schema must be strict JSON. JSON comments and trailing commas are invalid even though Liquid surrounds the object.

{% schema %}
{
  "name": "Promotional hero",
  "tag": "section",
  "class": "section-promo-hero",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Build a better storefront"
    },
    {
      "type": "richtext",
      "id": "body",
      "label": "Body",
      "default": "<p>Give merchants control over the copy, destination, and image.</p>"
    },
    {
      "type": "text",
      "id": "button_text",
      "label": "Button label",
      "default": "Shop the collection"
    },
    {
      "type": "url",
      "id": "button_url",
      "label": "Button link"
    },
    {
      "type": "image_picker",
      "id": "image",
      "label": "Image"
    }
  ],
  "presets": [
    {
      "name": "Promotional hero"
    }
  ]
}
{% endschema %}

Each ID is unique within the section and matches the property referenced in Liquid. Shopify documents the required setting attributes and value formats in its input settings reference.

Copy the complete section file

After conversion, inspect both the Schema and Liquid tabs. Use Download .liquid to save the combined production file, or copy an individual tab while debugging. Name the file descriptively, for example promo-hero.liquid; section filenames belong in the theme's sections directory.

If you want to design schema without source HTML, use the schema builder. For Liquid syntax experiments, open the Liquid cheatsheet and playground.

Test in a development theme

Do not test first in the published theme.

  1. Duplicate the theme or use a Shopify development theme.
  2. Add the downloaded file under sections/.
  3. Open a JSON-template page in the theme editor and add the new section.
  4. Change every setting, including blank values and unusually long text.
  5. Select an image with useful alt text and check its rendered dimensions.
  6. Test the link with no URL, an internal resource, and an external URL.
  7. Check narrow and wide viewports for overflow and layout shift.
  8. Run Shopify Theme Check and inspect the browser console.

Shopify's guide to sections and blocks explains how presets make a section available in the editor. The theme-editor integration guide covers editor attributes and lifecycle behavior.

Diagnose common failures

The section is absent from “Add section.” Confirm that the schema contains a presets array and that the file is inside sections/.

The theme editor reports invalid JSON. Copy only the object between the schema tags into a JSON validator. Remove comments, trailing commas, and duplicated keys.

Text appears as nested paragraphs. Render a richtext setting directly inside a neutral container such as <div class="rte">; do not place it inside <p>.

A setting changes nothing. Compare its schema ID with the Liquid reference character by character. button_url and button_link are different IDs.

The first image loads slowly. Above-the-fold images might need loading: 'eager' and fetchpriority: 'high', while later images should remain lazy. Make that decision from the section's actual template position rather than applying eager loading everywhere.

The result is ready when merchants can understand every control, blank states do not produce broken markup, the schema parses as JSON, and the section behaves correctly in a development theme.

Found this helpful?

Share it with your network!

Ready to Convert HTML to Liquid?

Try our free HTML to Liquid converter and build your Shopify themes faster.

Try HTML2Liquid Now