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.
Practice in the converter workspace
Optional utility after you understand sections—draft Liquid with schema and blocks.
Share
Share this guide
Found this guide useful? Share it with other Shopify developers on LinkedIn, X, or Reddit.