Skip to main content
Back to Blog
How to Convert HTML into a Shopify Section (Step-by-Step Guide)
Tutorial

How to Convert HTML into a Shopify Section (Step-by-Step Guide)

A complete, beginner-friendly walkthrough for turning a static HTML block into a fully editable Shopify section — with schema, settings, blocks, and presets you can paste straight into your theme.

12 min read

Static HTML is great for mockups, but it doesn't belong in a live Shopify theme. The moment a merchant wants to change a headline, swap an image, or recolor a button, they shouldn't have to open a code editor. The fix is to turn that HTML into a Shopify section — a self-contained, reusable block whose content is editable from the theme editor.

This guide walks through the entire process with a real example: we'll take a plain HTML hero banner and convert it into a production-ready section with settings, blocks, and a preset. By the end you'll understand exactly what every part of a section file does — and how to do it in seconds with the HTML to Liquid converter.

What is a Shopify section?

A section is a .liquid file in your theme's sections/ folder that combines three things:

  1. Liquid markup — your HTML, with dynamic values pulled from settings.
  2. A {% schema %} block — a JSON definition of the settings, blocks, and presets the merchant can configure.
  3. Optional scoped CSS/JS — styles and scripts unique to the section.

Sections power Online Store 2.0 themes. Merchants add, reorder, and customize them in the theme editor without touching code. If you want a quick reference for the tags and objects used below, keep the Liquid cheatsheet open in another tab.

Step 1 — Start with clean, structured HTML

Here's the static hero we want to make editable:

<div class="hero">
  <span class="hero__badge">New Collection</span>
  <h1>Summer Essentials</h1>
  <p>Lightweight pieces designed for warm days and cool nights.</p>
  <a href="/collections/summer" class="hero__button">Shop the collection</a>
  <img src="hero.jpg" alt="Summer collection flat lay">
</div>

Before converting, make sure your HTML is:

  • Well-formed — every tag is closed and properly nested.
  • Semantic — use h1, p, a, img rather than a pile of divs. The cleaner the structure, the better the conversion.
  • Free of inline content you'll want to edit — hard-coded text, image paths, and URLs are exactly what we'll turn into settings.

Step 2 — Identify what should be editable

Walk through the markup and mark every piece a merchant might reasonably want to change:

Element Becomes a setting of type
Badge text ("New Collection") text
Heading ("Summer Essentials") text
Subtext paragraph richtext
Button label text
Button link url
Hero image image_picker
Background / text color color

This mapping is the heart of the conversion. Each editable element gets a schema setting with a unique id, and you reference that id in the markup with section.settings.<id>.

Step 3 — Replace static content with Liquid

Swap each hard-coded value for its setting. Note three best practices baked into the example below:

  • Use {% if section.settings.x != blank %} so empty fields don't render stray markup.
  • Always pass a width (or height) to the image_url filter — Shopify returns an error if you provide neither — and prefer image_tag, which generates a responsive srcset automatically.
  • Escape attribute values like alt with the escape filter.
<div class="hero" style="background: {{ section.settings.bg_color }}; color: {{ section.settings.text_color }};">
  {% if section.settings.badge != blank %}
    <span class="hero__badge">{{ section.settings.badge }}</span>
  {% endif %}

  <h1>{{ section.settings.heading }}</h1>

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

  {% if section.settings.button_label != blank and section.settings.button_url != blank %}
    <a href="{{ section.settings.button_url }}" class="hero__button">
      {{ section.settings.button_label }}
    </a>
  {% endif %}

  {% if section.settings.image %}
    {{
      section.settings.image
      | image_url: width: 1600
      | image_tag: loading: 'lazy', alt: section.settings.image.alt
    }}
  {% endif %}
</div>

Step 4 — Write the {% schema %} block

The schema defines the settings UI. A few rules to remember: a section can have only one {% schema %} tag, it must contain valid JSON, and it can't be nested inside another Liquid tag. Place it at the bottom of the file.

{% schema %}
{
  "name": "Hero Banner",
  "tag": "section",
  "class": "section-hero",
  "settings": [
    { "type": "text", "id": "badge", "label": "Badge text", "default": "New Collection" },
    { "type": "text", "id": "heading", "label": "Heading", "default": "Summer Essentials" },
    { "type": "richtext", "id": "subheading", "label": "Subheading" },
    { "type": "text", "id": "button_label", "label": "Button label", "default": "Shop now" },
    { "type": "url", "id": "button_url", "label": "Button link" },
    { "type": "image_picker", "id": "image", "label": "Hero image" },
    { "type": "color", "id": "bg_color", "label": "Background color", "default": "#0f172a" },
    { "type": "color", "id": "text_color", "label": "Text color", "default": "#ffffff" }
  ],
  "presets": [
    { "name": "Hero Banner" }
  ]
}
{% endschema %}

Two attributes worth calling out:

  • presets make the section available as a dynamic section in the theme editor's "Add section" menu. Without a preset, the section can only be rendered statically with {% section 'hero-banner' %}.
  • default gives every setting a sensible starting value so the section renders correctly before the merchant touches anything.

There are many more setting types (range, select, video, collection, product, color_scheme, text_alignment, metaobject, and more) and section attributes (limit, enabled_on, disabled_on, locales). The fastest way to generate a valid schema for any layout is the Schema Generator.

Step 5 — Add blocks for repeatable content

Settings are great for fixed fields, but when a merchant needs to add an arbitrary number of items — feature bullets, logos, slides — you want blocks. Blocks are merchant-addable, reorderable mini-components defined in the schema and looped in the markup.

Add a blocks array to the schema:

{% schema %}
{
  "name": "Hero Banner",
  "settings": [ /* ... as above ... */ ],
  "blocks": [
    {
      "type": "feature",
      "name": "Feature",
      "settings": [
        { "type": "text", "id": "text", "label": "Feature", "default": "Free shipping over $50" }
      ]
    }
  ],
  "max_blocks": 4,
  "presets": [
    {
      "name": "Hero Banner",
      "blocks": [{ "type": "feature" }, { "type": "feature" }]
    }
  ]
}
{% endschema %}

Then render the blocks. The {{ block.shopify_attributes }} output is required — it's what lets the theme editor select and drag-and-drop each block:

{% if section.blocks.size > 0 %}
  <ul class="hero__features">
    {% for block in section.blocks %}
      <li {{ block.shopify_attributes }}>{{ block.settings.text }}</li>
    {% endfor %}
  </ul>
{% endif %}

Tip: Modern themes also support theme blocks — reusable blocks that live in the blocks/ folder and are rendered with {% content_for 'blocks' %}. They can be nested and shared across sections. For a single section like this, local blocks are perfectly fine.

Step 6 — Scope your CSS to the section

To avoid styles leaking between section instances, scope them with the section's unique ID using the {% style %} tag:

{% style %}
  #shopify-section-{{ section.id }} .hero {
    display: grid;
    gap: 16px;
    padding: 64px 24px;
    text-align: center;
  }
  #shopify-section-{{ section.id }} .hero__button {
    display: inline-block;
    padding: 12px 24px;
    border-radius: 8px;
    background: {{ section.settings.text_color }};
    color: {{ section.settings.bg_color }};
  }
{% endstyle %}

Step 7 — Test in the theme editor

Before going live, run through this checklist:

  1. Upload the section file to a development theme (never edit the live theme directly).
  2. In the theme editor, add the section to a page via Add section.
  3. Toggle every setting — text, image, URL, colors — and confirm the markup updates.
  4. Add, reorder, and remove blocks to confirm shopify_attributes is working.
  5. Check responsive behavior on mobile and tablet.
  6. Confirm the section renders correctly with only default values (no merchant input).

If something doesn't render, the usual culprits are invalid JSON in the schema, a duplicate setting id, or a missing closing Liquid tag. Running shopify theme check from the Shopify CLI will catch most of these automatically.

Do it instantly with HTML2Liquid

Everything above is exactly what the HTML to Liquid converter automates. Paste your HTML, choose Manual for a quick rules-based conversion or AI for smarter schema generation, and you'll get the Liquid markup and a matching {% schema %} in seconds. From there you can fine-tune the settings in the Schema Generator and validate your Liquid against the cheatsheet.

Conclusion

Converting HTML to a Shopify section comes down to a repeatable pattern: identify editable content, map each piece to a setting, replace static values with section.settings, add blocks for repeatable items, and define a schema with sensible defaults and a preset. Master that pattern once and every future section becomes fast, predictable, and merchant-friendly.

Next steps: learn the full section schema reference, explore every Liquid filter and object, and try converting your first section with the free converter.

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