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:
- Liquid markup — your HTML, with dynamic values pulled from settings.
- A
{% schema %}block — a JSON definition of the settings, blocks, and presets the merchant can configure. - 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,imgrather than a pile ofdivs. 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(orheight) to theimage_urlfilter — Shopify returns an error if you provide neither — and preferimage_tag, which generates a responsivesrcsetautomatically. - Escape attribute values like
altwith theescapefilter.
<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:
presetsmake 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' %}.defaultgives 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:
- Upload the section file to a development theme (never edit the live theme directly).
- In the theme editor, add the section to a page via Add section.
- Toggle every setting — text, image, URL, colors — and confirm the markup updates.
- Add, reorder, and remove blocks to confirm
shopify_attributesis working. - Check responsive behavior on mobile and tablet.
- 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.



