← Back to guides
TutorialLiquid BasicsLiquid · Published 2024-05-19 · 3 min read

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 rows
  • offset: skips leading items when one is featured elsewhere
  • reversed iterates 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.