Shopify Liquid Loops: for, limit, offset & pagination
Master collection loops, block loops, and pagination—the patterns you will use on every theme.
Share with Shopify developers — useful guides spread faster in theme dev communities.
Two loop contexts you will use daily
{% for block in section.blocks %} powers merchant-editable rows. {% for product in collection.products %} powers catalog templates. Confusing them causes blocks that ignore merchant rows or product loops that cap silently at 50.
Basic for loop
{% for block in section.blocks %}
<div {{ block.shopify_attributes }}>
<h3>{{ block.settings.title | escape }}</h3>
</div>
{% else %}
<p class="empty-state">Add items in the theme editor.</p>
{% endfor %}
The {% else %} branch runs when the array is empty—prevents blank sections after merchants delete all blocks.
limit, offset, reversed
{% for product in collection.products limit: 4 offset: 1 %}
{% render 'product-card', product: product %}
{% endfor %}
limit:caps iterations—use on homepage collection rowsoffset:skips leading items when one is featured elsewherereversediterates last-to-first without changing the source
forloop object
forloop.index, forloop.first, forloop.last, and forloop.length help with numbering, active tab states, and omitting trailing dividers.
break and continue
{% for product in collection.products %}
{% if product.available == false %}
{% continue %}
{% endif %}
{% render 'product-card', product: product %}
{% if forloop.index == 8 %}
{% break %}
{% endif %}
{% endfor %}
Prefer limit: over heavy break logic when possible—easier to read in code review.
Pagination at scale
{% paginate collection.products by 24 %}
{% for product in collection.products %}
{% render 'card-product', product: product %}
{% endfor %}
{% if paginate.pages > 1 %}
{{ paginate | default_pagination }}
{% endif %}
{% endpaginate %}
Takeaway
Blocks for merchant rows with shopify_attributes; paginate for catalogs; guard empty arrays. Block architecture: Reusable Sections.
Block loops vs collection loops
{% for block in section.blocks %} powers merchant-editable rows—always include {{ block.shopify_attributes }}. {% for product in collection.products %} powers catalog templates—always paginate at scale. Confusing the two causes blocks that render products or product loops that ignore merchant rows.
else and empty states
{% for block in section.blocks %}
...
{% else %}
<p class="empty-state">Add FAQ items in the theme editor.</p>
{% endfor %}
Empty states prevent blank sections in the editor when merchants delete all blocks.
Performance guardrails
- Use
limit:on homepage collection rows - Avoid nested product loops inside block loops
- Do not duplicate
<script>tags per iteration
Debugging
Loop renders zero items: empty collection, wrong block type filter, or missing paginate.
Only first 50 products: add pagination.
Editor cannot select a block: missing shopify_attributes.
Topic cluster
Liquid Basics
Objects, tags, filters, loops, and beginner Liquid workflows.
Convert HTML to Shopify Liquid
Paste HTML & generate Liquid with schema, blocks, and scoped CSS. No signup required.
Share
Share this guide
Found this guide useful? Share it with other Shopify developers on LinkedIn, X, or Reddit.