Static HTML describes one fixed result. A Shopify section must describe both the storefront markup and the controls a merchant can safely change. Good conversion work is therefore less about replacing every string with {{ ... }} and more about choosing a stable content model.
Use the HTML converter for the mechanical first pass, then review the output with the following practices.
Decide what should actually be editable
Expose content that a merchant is likely to change: headings, supporting copy, images, destinations, labels, colors with a real design purpose, and repeated items. Do not create settings for structural class names, accessibility attributes, every pixel value, or content owned by Shopify resources.
For this HTML:
<div class="feature-card">
<img src="shipping.svg" alt="Delivery truck">
<h3>Fast delivery</h3>
<p>Show estimated delivery information near the product.</p>
</div>
the image, heading, and body are useful settings. The feature-card class should remain code because allowing arbitrary class changes would make the section fragile.
Match setting types to rendered values
Use text for one-line plain text and escape it when rendering:
{% if section.settings.heading != blank %}
<h2>{{ section.settings.heading | escape }}</h2>
{% endif %}
Use richtext when merchants need links, emphasis, or paragraphs. Shopify returns HTML for this setting, so render it without a paragraph wrapper:
{% if section.settings.body != blank %}
<div class="rte">{{ section.settings.body }}</div>
{% endif %}
Use separate text and url settings for a call to action. Guard both so the section does not output an empty interactive element:
{% if section.settings.button_text != blank and section.settings.button_url != blank %}
<a class="button" href="{{ section.settings.button_url }}">
{{ section.settings.button_text | escape }}
</a>
{% endif %}
The official input setting documentation describes the runtime value returned by each type.
Treat schema as strict JSON
The contents of {% schema %} are JSON, not JavaScript. Comments, trailing commas, and duplicate keys are invalid.
{% schema %}
{
"name": "Feature card",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Fast delivery"
},
{
"type": "richtext",
"id": "body",
"label": "Body",
"default": "<p>Show estimated delivery information near the product.</p>"
},
{
"type": "image_picker",
"id": "image",
"label": "Image"
}
],
"presets": [
{
"name": "Feature card"
}
]
}
{% endschema %}
IDs must be unique within the section. Block setting IDs must likewise be unique within each block definition. Keep IDs deterministic and descriptive so future changes do not disconnect values already stored in JSON templates. Shopify's section schema reference documents these rules.
Use blocks only for merchant-managed repetition
If the HTML contains three feature cards, do not generate three groups of heading_1, heading_2, and heading_3 settings. Define one block type and loop over section.blocks:
<div class="feature-grid">
{% for block in section.blocks %}
<article class="feature-card" {{ block.shopify_attributes }}>
{% if block.settings.heading != blank %}
<h3>{{ block.settings.heading | escape }}</h3>
{% endif %}
{% if block.settings.body != blank %}
<div class="rte">{{ block.settings.body }}</div>
{% endif %}
</article>
{% endfor %}
</div>
{{ block.shopify_attributes }} connects the wrapper to selection and reordering in the theme editor. Add sample blocks to the section preset only when their defaults create a useful initial state.
Render responsive images through Shopify
Do not preserve a hardcoded uploaded-image URL. An image_picker returns an image object that can be transformed through Shopify's CDN:
{% if section.settings.image != blank %}
{{
section.settings.image
| image_url: width: 1800
| image_tag:
widths: '480, 750, 1100, 1500, 1800',
sizes: '(min-width: 990px) 50vw, 100vw',
loading: 'lazy',
alt: section.settings.image.alt
}}
{% endif %}
Set a realistic maximum width, provide widths and sizes, and preserve useful alt text. Only make an image eager when the section is known to be above the fold.
Do not copy executable HTML blindly
Remove <script> elements, onclick and other event-handler attributes, and untrusted inline code. Generated Liquid should escape plain merchant text. Rich text is intentionally rendered as HTML because Shopify controls the editor's output format; it should not be passed through escape or placed in an HTML attribute.
Video URLs need special treatment: Shopify's video setting represents a hosted video object, while video_url represents an accepted YouTube or Vimeo URL. An iframe needs the provider type and video ID rather than using the watch-page URL directly.
Keep accessible semantics intact
Conversion should not turn every call to action into a button. Use an anchor for navigation and a button for an in-page action. Preserve the heading hierarchy relative to the template in which the section will be inserted; a reusable section generally should not assume it owns the page's only <h1>.
Test keyboard focus, visible focus styles, link purpose, image alternatives, zoom, and text reflow. Avoid empty links and duplicated form IDs.
Review and test before publishing
Use this checklist in a Shopify development theme:
- Parse the schema object with a JSON validator.
- Run Shopify Theme Check on the section file.
- Add, remove, duplicate, and reorder every block type.
- Test blank settings and unusually long translated content.
- Confirm image dimensions and loading behavior in browser developer tools.
- Navigate all controls with a keyboard.
- Check mobile, tablet, and desktop widths.
- Inspect the rendered HTML for nested paragraphs and empty anchors.
- Reload the theme editor after changing IDs to detect lost setting values.
- Preview the exact JSON template that will use the section.
The schema builder can help design and lint settings, while the Liquid cheatsheet is useful for testing filters and object access. For a full end-to-end example, continue with How to convert HTML into a Shopify section.
A successful conversion is not the one with the most settings. It is the one whose schema is valid, whose markup is safe and accessible, and whose controls match how a merchant actually edits the section.