If you’ve ever dabbled in Shopify theme development or custom liquid-powered projects, chances are you’ve run into Liquid loops. As someone who’s been a Liquid supplier for years, I’ve lost count of how many developers message me asking why their loops won’t render products, collections, or blog posts the way they want. The truth is, Liquid loops aren’t as tricky as they seem—once you wrap your head around the basics, you’ll be pulling dynamic content faster than you can swap a theme’s accent color. Liquid

First, let’s start with the absolute basics: what even is a Liquid loop, and why does it matter for your work? Liquid is Shopify’s templating language, built to pull dynamic content from your store’s admin into your front-end. Loops (officially called “iterations” in Liquid terms) let you repeat a block of code for every item in a set—like every product in a collection, every blog post on a homepage, or every variant of a single product. Without loops, you’d have to hardcode each item one by one, which is a nightmare to update if you add new products or posts. As a supplier, I work with developers all the time who come to me because their Liquid loops are broken; usually, it’s a small syntax mistake that’s easy to fix. Let’s break down the most common loop types you’ll use, and how to build them without the headache.
The granddaddy of Liquid loops is the for loop. If you’ve ever used a loop in any other coding language, this will feel familiar, but Liquid’s flavor has its own little quirks. The basic structure is dead simple: start with {% for item in collection %} (where collection is whatever set you’re pulling from), then your HTML or Liquid code, and end with {% endfor %}. For example, if you want to display the first 4 products from your featured collection, that loop would look like this:
{% for product in collections.featured.products limit:4 %}
{{ product.title }}
${{ product.price | money }}
{% endfor %}
Wait, notice that limit:4 I slipped in? That’s a parameter you can add to the for tag to control how many items the loop runs. Super handy if you don’t want to flood your homepage with 20 products. There are a few other useful parameters too: offset:2 would skip the first 2 items, so your loop starts at the third one, and reversed flips the order so the last item comes first. I’ve had developers come to me saying their loop is pulling too many products; 9 times out of 10, they forgot to add the limit tag, or they used the wrong collection handle (that’s the part of the collection URL, like featured, not the full name like “Summer 2024 Collection”).
Another common mistake with for loops is forgetting the item variable in the loop name. If you write {% for in collections.featured.products %} instead of {% for product in collections.featured.products %}, Liquid won’t know what to call each item in the set, and you’ll get a weird error or nothing at all. It’s a tiny typo, but one that trips up everyone, even the seasoned devs I work with.
Next up: the for loop has a bunch of built-in helper variables that are game-changers. Want to show that this is the first product in the loop? Use {% if forloop.first %} inside your loop to add a special class or text. If you want to add a border between products except the last one, {% if forloop.last %} lets you skip that border. You can even count the loop number with {{ forloop.index }}—so the first product is index 1, the second is 2, and so on. If you start counting from zero, use forloop.index0 instead. These helpers make your loops dynamic, not just a list of identical elements. For example, if you want to add a “New!” badge to the first 2 products in your collection, you’d do:
{% for product in collections.all.products limit:2 %}
{{ product.title }}
${{ product.price | money }}
{% endfor %}
As a Liquid supplier, I often recommend playing with these helper variables first because they’re the easiest way to make your front-end look polished without extra code. A lot of devs don’t even know these exist, so they end up writing extra if-else statements when Liquid already has them built in.
Now, what if you’re not looping through a collection or product set? What if you want to loop through a custom list, like a menu, blog tags, or even a static list of links? Liquid has you covered. For menu items, the loop would look like {% for link in linklists.main.links %}, which pulls the items from your main navigation menu in the admin. For blog tags, it’s {% for tag in blogs.blog.tags %}. If you want to loop through a custom list you’ve defined, like a list of footer links, you can make an array right in your Liquid code:
{% assign footer_links = "Home,Shop,About,Contact" | split: ", " %}
{% for link in footer_links %}
{{ link }}
{% endfor %}
That split filter turns that comma-separated string into an array, so the loop can run through each item. I use this all the time for small, static lists that don’t need to be in the store admin, like footer links or quick links. It’s way simpler than hardcoding each anchor tag separately.
Another loop type you might run into is the cycle tag. This one is super useful for alternating styles, like making every other product card a different background color. The cycle tag will toggle between values each time the loop runs. For example:
{% for product in collections.featured.products %}
{{ product.title }}
${{ product.price | money }}
{% endfor %}
That’s way better than using forloop.index and modulo math, which is what a lot of people try first. The cycle tag is clean, easy to read, and works every time. I’ve seen so many devs waste time writing complex modulo code when cycle does the same job in one line.
Wait, what about loops that don’t work? Let’s talk about the most common issues I see as a Liquid supplier. The first one is incorrect handles. Every collection, product, and set has a handle, which is the lowercase, no-space version of its name. If your collection is called “Summer 2024 Collection”, its handle is summer-2024-collection, not the full name. If you use the full name in your loop, Liquid will throw an error because it can’t find that collection. I always tell devs to check their handle by going to their Shopify admin, clicking on Products > Collections, and looking at the URL when you edit a collection—whatever comes after collection/ is the handle.
The second big issue is empty sets. If you write a loop for a collection that has no products, or a blog that has no posts, Liquid will just show nothing, no error. That can be confusing if you’re expecting content to show up. To fix that, add an else clause right after your loop:
{% for product in collections.summer-2024-collection.products %}
{% endfor %}
{% else %}
No products in this collection yet!
{% endfor %}
That’s a lifesaver for testing, because you’ll immediately know if your collection is empty, not just if your loop is broken. As a supplier, I suggest adding an else to every loop when you’re building a theme or custom section—saves so much debugging time.
Another common pitfall is nested loops. Want to loop through products and then loop through each product’s variants? That’s a nested loop, and it’s easy to mess up. Let’s say you want to show each product’s variants as small thumbnails:
{% for product in collections.featured.products %}
{{ product.title }}
${{ product.price | money }}
{{ variant.title }}
{% endfor %}
{% endfor %}
The key here is that the inner loop’s variable (variant) doesn’t conflict with the outer loop’s variable (product). If you accidentally use the same variable name, like {% for item in product.variants %}, Liquid will overwrite the outer item variable, and you’ll get errors. I’ve done that myself more times than I can count—always name your inner loop variables something different from the outer ones.
Now, let’s move to some pro tips that I’ve picked up over years as a Liquid supplier. First, if you need to loop through a large number of items, Liquid has a limit of how many items a for loop can run by default (I think it’s 50, but don’t quote me on that). If you need more than 50 products, you can use the all parameter, like {% for product in collections.all.products all %}, but be careful—loops that run through hundreds of items can slow down your page, because Liquid has to pull all those items from the store’s database. It’s better to use limit to keep loops small, only grabbing what you need.
Second, you can sort the items in your loop! By default, loops pull items in the order they’re set in the store admin, but you can add a sort_by parameter to change that. For example, {% for product in collections.featured.products sort_by: 'price' %} will sort products from lowest to highest price. To sort highest to lowest, add sort_by: 'price' | reverse. You can sort by title, date, product type, and more. That’s another helper a lot of devs don’t know about—you can rearrange your loop’s order without going into the Shopify admin to reorder products manually.
Third, if you’re building a custom section for Shopify’s Online Store 2.0, loops work the same way, but you can let merchants choose which collection to display in the Customizer. You’d use a product or collection block, and then your loop would pull from the selected collection, like {% for product in section.settings.collection.products limit:4 %}. That way, a merchant can change the featured collection without touching any code. I work with a lot of small business owners who don’t know how to edit code, so building loops that are customizable in the admin makes their lives way easier, and it’s a skill I always recommend to developers.
Let’s wrap this up with a real example that ties all these tips together. Say you want to build a homepage section that shows 3 products from a merchant’s best-selling collection, with a “Best Seller” badge on the first product, alternating background colors, and a message if the collection is empty. Here’s what that code would look like:
{% comment %} This section pulls from a merchant-selected best-sellers collection, customizable in the admin {% endcomment %}
{% assign collection = section.settings.best_sellers_collection %}
{% if collection %}
Our Best Sellers
{% for product in collection.products limit:3 sort_by: ‘price’ %}
{{ product.title }}
${{ product.price | money }}
{% endfor %}
{% else %}
Please select a best-sellers collection in the theme settings.
{% endif %}
That’s a clean, dynamic loop that works for any merchant, no code changes needed. As a Liquid supplier, I help developers build these kinds of reusable, user-friendly loops all the time—they’re the bread and butter of Shopify front-end work.
At the end of the day, Liquid loops are just tools to make your life easier. The most important thing is to test your loops with real data, add those else clauses for empty sets, and double-check your handles and variable names. If you’re new to Liquid, start with simple for loops, mess around with the limit and sort_by parameters, and don’t be afraid to break something—even if your loop returns nothing, it’s just a syntax fix away.

If you’re building a custom theme, need help troubleshooting a tricky loop, or want reliable Liquid assets to speed up your development work, I’m here to help. We can work through your specific needs, answer questions about loop structure, or provide pre-built Liquid snippets to cut down your build time. Reach out to my team to start a conversation about your project, and let’s make your Liquid loops work for you.
EU Warehouse References: Shopify Liquid Documentation (For Loops), Shopify Theme Development Guide, Liquid Iteration Helpers.
Boostvapes Wholesale
As one of the most professional liquid manufacturers and suppliers in China, we offer a wide range of products with superior quality. Please rest assured to buy discount liquid in stock here from our factory. For price consultation, contact us.
Address: Room 2, 3/F, Ruby Commercial Building, 480 Nathan Road, Kowloon, Hongkong, China
E-mail: judy@baishvape.com
WebSite: https://www.boost-vapes.com/