Almost every Shopify section that shows more than one product is a for loop over a collection: featured products, related items, a category grid. The loop itself is three lines; doing it the production way is knowing the limits and the edge cases.
The basic loop
<ul class="product-grid"> {% for product in collection.products %} <li> <a href="{{ product.url }}">{{ product.title }}</a> <span>{{ product.price | money }}</span> </li> {% endfor %}</ul>Limit how many you render
You rarely want every product in a collection on a homepage strip. Cap it with limit:
{% for product in collection.products limit: 4 %} {# ...renders at most 4 products #}{% endfor %}The forloop object
Inside any loop, Liquid gives you a forloop object with useful state, handy for "first" styling, numbering, or stopping early:
{% for product in collection.products limit: 4 %} <li class="{% if forloop.first %}is-featured{% endif %}"> {{ forloop.index }}. {{ product.title }} </li>{% endfor %}forloop.index, current iteration, starting at 1.forloop.index0, same, starting at 0.forloop.first/forloop.last, booleans for the edges.forloop.length, total iterations.
Always handle the empty case
A collection can be empty, newly created, fully sold out, or filtered to nothing. Liquid's for...else gives you a clean empty state:
{% for product in collection.products %} <li>{{ product.title }}</li>{% else %} <p>No products in this collection yet.</p>{% endfor %}Don't loop a whole large collection
Looping collection.products without a limit renders at most 50 products and silently drops the rest, and it's slow. For a full collection page that needs every product, use the paginate tag and loop the paginated set instead. limit is for fixed, small strips; pagination is for full listings.
What AI tools get wrong here
AI suggestions routinely skip the empty state (so an empty collection renders a bare, broken section) and loop large collections without pagination (so products silently vanish past the 50-item cap). Reaching for for...else and knowing when to paginate is the production-grade instinct.
Learn this properly · free lesson
Build a 'Featured Collection' section from the newest products
Build a featured-collection section with a real for loop in the interactive editor, limit, index, and empty state included. Free lesson, no signup wall.
Try this lesson for freeFrequently asked questions
How do I limit the number of products in a Liquid loop?
Add limit to the for tag: {% for product in collection.products limit: 4 %}. It renders at most that many products.
How do I get the loop index in Liquid?
Use the forloop object: forloop.index (starts at 1), forloop.index0 (starts at 0), plus forloop.first, forloop.last, and forloop.length.
How do I show a message when a collection is empty?
Use for...else: put your normal markup in the for block and the empty-state markup after {% else %}. It renders only when the collection has no products.
Why are some products missing from my loop?
Looping collection.products directly returns at most 50 items. For a full collection listing, wrap it in the paginate tag and loop the paginated collection so every product renders across pages.
- Liquid
- Loops
- Collections
About the author
Bas Lefeber, Founder, learnshopify.dev
Bas builds learnshopify.dev, where developers learn production-grade Shopify theme development against a live storefront. He writes about Liquid, theme architecture, and the parts of the job that still matter now that AI writes the code.
Keep going in the curriculum

