← 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.

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.