{"id":3682,"date":"2026-11-10T11:12:43","date_gmt":"2026-11-10T03:12:43","guid":{"rendered":"http:\/\/www.canadacontabil.com\/blog\/?p=3682"},"modified":"2026-11-10T11:12:43","modified_gmt":"2026-11-10T03:12:43","slug":"how-do-i-create-loops-in-liquid-44c5-b6d400","status":"publish","type":"post","link":"http:\/\/www.canadacontabil.com\/blog\/2026\/11\/10\/how-do-i-create-loops-in-liquid-44c5-b6d400\/","title":{"rendered":"How do I create loops in Liquid?"},"content":{"rendered":"<p>If you\u2019ve ever dabbled in Shopify theme development or custom liquid-powered projects, chances are you\u2019ve run into Liquid loops. As someone who\u2019s been a Liquid supplier for years, I\u2019ve lost count of how many developers message me asking why their loops won\u2019t render products, collections, or blog posts the way they want. The truth is, Liquid loops aren\u2019t as tricky as they seem\u2014once you wrap your head around the basics, you\u2019ll be pulling dynamic content faster than you can swap a theme\u2019s accent color. <a href=\"https:\/\/www.boost-vapes.com\/liquid\/\">Liquid<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.boost-vapes.com\/uploads\/47764\/small\/bood-pulse-storm-12k-disposable-vape-in-stockb4a3f.jpg\"><\/p>\n<p>First, let\u2019s start with the absolute basics: what even is a Liquid loop, and why does it matter for your work? Liquid is Shopify\u2019s templating language, built to pull dynamic content from your store\u2019s admin into your front-end. Loops (officially called \u201citerations\u201d in Liquid terms) let you repeat a block of code for every item in a set\u2014like every product in a collection, every blog post on a homepage, or every variant of a single product. Without loops, you\u2019d 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\u2019s a small syntax mistake that\u2019s easy to fix. Let\u2019s break down the most common loop types you\u2019ll use, and how to build them without the headache.<\/p>\n<p>The granddaddy of Liquid loops is the <code>for<\/code> loop. If you\u2019ve ever used a loop in any other coding language, this will feel familiar, but Liquid\u2019s flavor has its own little quirks. The basic structure is dead simple: start with <code>{% for item in collection %}<\/code> (where <code>collection<\/code> is whatever set you\u2019re pulling from), then your HTML or Liquid code, and end with <code>{% endfor %}<\/code>. For example, if you want to display the first 4 products from your featured collection, that loop would look like this:<br \/>\n{% for product in collections.featured.products limit:4 %}<\/p>\n<div class=\"product-card\">\n    <img decoding=\"async\" src=\"{{ product.image | product_image_url }}\" alt=\"{{ product.title }}\"><\/p>\n<h3>{{ product.title }}<\/h3>\n<p>${{ product.price | money }}<\/p>\n<\/p><\/div>\n<p>{% endfor %}<\/p>\n<p>Wait, notice that <code>limit:4<\/code> I slipped in? That\u2019s a parameter you can add to the <code>for<\/code> tag to control how many items the loop runs. Super handy if you don\u2019t want to flood your homepage with 20 products. There are a few other useful parameters too: <code>offset:2<\/code> would skip the first 2 items, so your loop starts at the third one, and <code>reversed<\/code> flips the order so the last item comes first. I\u2019ve had developers come to me saying their loop is pulling too many products; 9 times out of 10, they forgot to add the <code>limit<\/code> tag, or they used the wrong collection handle (that\u2019s the part of the collection URL, like <code>featured<\/code>, not the full name like \u201cSummer 2024 Collection\u201d).<\/p>\n<p>Another common mistake with <code>for<\/code> loops is forgetting the <code>item<\/code> variable in the loop name. If you write <code>{% for in collections.featured.products %}<\/code> instead of <code>{% for product in collections.featured.products %}<\/code>, Liquid won\u2019t know what to call each item in the set, and you\u2019ll get a weird error or nothing at all. It\u2019s a tiny typo, but one that trips up everyone, even the seasoned devs I work with.<\/p>\n<p>Next up: the <code>for<\/code> 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 <code>{% if forloop.first %}<\/code> inside your loop to add a special class or text. If you want to add a border between products except the last one, <code>{% if forloop.last %}<\/code> lets you skip that border. You can even count the loop number with <code>{{ forloop.index }}<\/code>\u2014so the first product is index 1, the second is 2, and so on. If you start counting from zero, use <code>forloop.index0<\/code> instead. These helpers make your loops dynamic, not just a list of identical elements. For example, if you want to add a \u201cNew!\u201d badge to the first 2 products in your collection, you\u2019d do:<br \/>\n{% for product in collections.all.products limit:2 %}<\/p>\n<div class=\"product-card {% if forloop.first %}featured-product{% endif %}\">\n    {% if forloop.first %}<span class=\"new-badge\">New!<\/span>{% endif %}<br \/>\n    <img decoding=\"async\" src=\"{{ product.image | product_image_url }}\" alt=\"{{ product.title }}\"><\/p>\n<h3>{{ product.title }}<\/h3>\n<p>${{ product.price | money }}<\/p>\n<\/p><\/div>\n<p>{% endfor %}<\/p>\n<p>As a Liquid supplier, I often recommend playing with these helper variables first because they\u2019re the easiest way to make your front-end look polished without extra code. A lot of devs don\u2019t even know these exist, so they end up writing extra if-else statements when Liquid already has them built in.<\/p>\n<p>Now, what if you\u2019re 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 <code>{% for link in linklists.main.links %}<\/code>, which pulls the items from your main navigation menu in the admin. For blog tags, it\u2019s <code>{% for tag in blogs.blog.tags %}<\/code>. If you want to loop through a custom list you\u2019ve defined, like a list of footer links, you can make an array right in your Liquid code:<br \/>\n{% assign footer_links = &quot;Home,Shop,About,Contact&quot; | split: &quot;, &quot; %}<br \/>\n{% for link in footer_links %}<br \/>\n<a href=\"\/{{ link | downcase }}\">{{ link }}<\/a><br \/>\n{% endfor %}<\/p>\n<p>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\u2019t need to be in the store admin, like footer links or quick links. It\u2019s way simpler than hardcoding each anchor tag separately.<\/p>\n<p>Another loop type you might run into is the <code>cycle<\/code> tag. This one is super useful for alternating styles, like making every other product card a different background color. The <code>cycle<\/code> tag will toggle between values each time the loop runs. For example:<br \/>\n{% for product in collections.featured.products %}<\/p>\n<div class=\"product-card {% cycle 'odd', 'even' %}\">\n    <img decoding=\"async\" src=\"{{ product.image | product_image_url }}\" alt=\"{{ product.title }}\"><\/p>\n<h3>{{ product.title }}<\/h3>\n<p>${{ product.price | money }}<\/p>\n<\/p><\/div>\n<p>{% endfor %}<\/p>\n<p>That\u2019s way better than using <code>forloop.index<\/code> and modulo math, which is what a lot of people try first. The <code>cycle<\/code> tag is clean, easy to read, and works every time. I\u2019ve seen so many devs waste time writing complex modulo code when <code>cycle<\/code> does the same job in one line.<\/p>\n<p>Wait, what about loops that don\u2019t work? Let\u2019s 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 \u201cSummer 2024 Collection\u201d, its handle is <code>summer-2024-collection<\/code>, not the full name. If you use the full name in your loop, Liquid will throw an error because it can\u2019t find that collection. I always tell devs to check their handle by going to their Shopify admin, clicking on Products &gt; Collections, and looking at the URL when you edit a collection\u2014whatever comes after <code>collection\/<\/code> is the handle.<\/p>\n<p>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\u2019re expecting content to show up. To fix that, add an <code>else<\/code> clause right after your loop:<br \/>\n{% for product in collections.summer-2024-collection.products %}<\/p>\n<div class=\"product-card\">&#8230;<\/div>\n<p>{% endfor %}<br \/>\n{% else %}<\/p>\n<p>No products in this collection yet!<\/p>\n<p>{% endfor %}<\/p>\n<p>That\u2019s a lifesaver for testing, because you\u2019ll immediately know if your collection is empty, not just if your loop is broken. As a supplier, I suggest adding an <code>else<\/code> to every loop when you\u2019re building a theme or custom section\u2014saves so much debugging time.<\/p>\n<p>Another common pitfall is nested loops. Want to loop through products and then loop through each product\u2019s variants? That\u2019s a nested loop, and it\u2019s easy to mess up. Let\u2019s say you want to show each product\u2019s variants as small thumbnails:<br \/>\n{% for product in collections.featured.products %}<\/p>\n<div class=\"product-card\">\n    <img decoding=\"async\" src=\"{{ product.image | product_image_url }}\" alt=\"{{ product.title }}\"><\/p>\n<h3>{{ product.title }}<\/h3>\n<p>${{ product.price | money }}<\/p>\n<div class=\"variants\">\n      {% for variant in product.variants %}<br \/>\n        <span class=\"variant-option\">{{ variant.title }}<\/span><br \/>\n      {% endfor %}\n    <\/div>\n<\/p><\/div>\n<p>{% endfor %}<\/p>\n<p>The key here is that the inner loop\u2019s variable (<code>variant<\/code>) doesn\u2019t conflict with the outer loop\u2019s variable (<code>product<\/code>). If you accidentally use the same variable name, like <code>{% for item in product.variants %}<\/code>, Liquid will overwrite the outer <code>item<\/code> variable, and you\u2019ll get errors. I\u2019ve done that myself more times than I can count\u2014always name your inner loop variables something different from the outer ones.<\/p>\n<p>Now, let\u2019s move to some pro tips that I\u2019ve 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 <code>for<\/code> loop can run by default (I think it\u2019s 50, but don\u2019t quote me on that). If you need more than 50 products, you can use the <code>all<\/code> parameter, like <code>{% for product in collections.all.products all %}<\/code>, but be careful\u2014loops that run through hundreds of items can slow down your page, because Liquid has to pull all those items from the store\u2019s database. It\u2019s better to use <code>limit<\/code> to keep loops small, only grabbing what you need.<\/p>\n<p>Second, you can sort the items in your loop! By default, loops pull items in the order they\u2019re set in the store admin, but you can add a <code>sort_by<\/code> parameter to change that. For example, <code>{% for product in collections.featured.products sort_by: 'price' %}<\/code> will sort products from lowest to highest price. To sort highest to lowest, add <code>sort_by: 'price' | reverse<\/code>. You can sort by title, date, product type, and more. That\u2019s another helper a lot of devs don\u2019t know about\u2014you can rearrange your loop\u2019s order without going into the Shopify admin to reorder products manually.<\/p>\n<p>Third, if you\u2019re building a custom section for Shopify\u2019s Online Store 2.0, loops work the same way, but you can let merchants choose which collection to display in the Customizer. You\u2019d use a <code>product<\/code> or <code>collection<\/code> block, and then your loop would pull from the selected collection, like <code>{% for product in section.settings.collection.products limit:4 %}<\/code>. That way, a merchant can change the featured collection without touching any code. I work with a lot of small business owners who don\u2019t know how to edit code, so building loops that are customizable in the admin makes their lives way easier, and it\u2019s a skill I always recommend to developers.<\/p>\n<p>Let\u2019s 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\u2019s best-selling collection, with a \u201cBest Seller\u201d badge on the first product, alternating background colors, and a message if the collection is empty. Here\u2019s what that code would look like:<br \/>\n{% comment %} This section pulls from a merchant-selected best-sellers collection, customizable in the admin {% endcomment %}<br \/>\n{% assign collection = section.settings.best_sellers_collection %}<br \/>\n{% if collection %}<\/p>\n<section class=\"best-sellers\">\n<h2>Our Best Sellers<\/h2>\n<p>    {% for product in collection.products limit:3 sort_by: &#8216;price&#8217; %}<\/p>\n<div class=\"product-card {% cycle 'light', 'gray' %}\">\n        {% if forloop.first %}<span class=\"badge\">Best Seller<\/span>{% endif %}<br \/>\n        <img decoding=\"async\" src=\"{{ product.image | product_image: 'medium' }}\" alt=\"{{ product.title }}\"><\/p>\n<h3>{{ product.title }}<\/h3>\n<p class=\"product-price\">${{ product.price | money }}<\/p>\n<p>        <a href=\"{{ product.url }}\" class=\"btn\">Shop Now<\/a>\n      <\/div>\n<p>    {% endfor %}<br \/>\n  <\/section>\n<p>{% else %}<\/p>\n<p>Please select a best-sellers collection in the theme settings.<\/p>\n<p>{% endif %}<\/p>\n<p>That\u2019s 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\u2014they\u2019re the bread and butter of Shopify front-end work.<\/p>\n<p>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 <code>else<\/code> clauses for empty sets, and double-check your handles and variable names. If you\u2019re new to Liquid, start with simple <code>for<\/code> loops, mess around with the <code>limit<\/code> and <code>sort_by<\/code> parameters, and don\u2019t be afraid to break something\u2014even if your loop returns nothing, it\u2019s just a syntax fix away.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.boost-vapes.com\/uploads\/47764\/small\/aaok-120k-puff-disposable-vape77069.jpg\"><\/p>\n<p>If you\u2019re building a custom theme, need help troubleshooting a tricky loop, or want reliable Liquid assets to speed up your development work, I\u2019m 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\u2019s make your Liquid loops work for you.<\/p>\n<p><a href=\"https:\/\/www.boost-vapes.com\/eu-warehouse\/\">EU Warehouse<\/a> References: Shopify Liquid Documentation (For Loops), Shopify Theme Development Guide, Liquid Iteration Helpers.<\/p>\n<hr>\n<p><a href=\"https:\/\/www.boost-vapes.com\/\">Boostvapes Wholesale<\/a><br \/>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.<br \/>Address: Room 2, 3\/F, Ruby Commercial Building, 480 Nathan Road, Kowloon, Hongkong, China<br \/>E-mail: judy@baishvape.com<br \/>WebSite: <a href=\"https:\/\/www.boost-vapes.com\/\">https:\/\/www.boost-vapes.com\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you\u2019ve ever dabbled in Shopify theme development or custom liquid-powered projects, chances are you\u2019ve run &hellip; <a title=\"How do I create loops in Liquid?\" class=\"hm-read-more\" href=\"http:\/\/www.canadacontabil.com\/blog\/2026\/11\/10\/how-do-i-create-loops-in-liquid-44c5-b6d400\/\"><span class=\"screen-reader-text\">How do I create loops in Liquid?<\/span>Read more<\/a><\/p>\n","protected":false},"author":91,"featured_media":3682,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[3645],"class_list":["post-3682","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-industry","tag-liquid-45fe-b70716"],"_links":{"self":[{"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/posts\/3682","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/users\/91"}],"replies":[{"embeddable":true,"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/comments?post=3682"}],"version-history":[{"count":0,"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/posts\/3682\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/posts\/3682"}],"wp:attachment":[{"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/media?parent=3682"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/categories?post=3682"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.canadacontabil.com\/blog\/wp-json\/wp\/v2\/tags?post=3682"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}