Skip to main content

Limit how many items a Liquid array fetches

Fetch only the items that you'll render. For collection.products and collections, a for loop's limit parameter is enough. Other Liquid arrays need the paginate tag.


Each product that a Liquid array loads carries database queries for variant data, images, pricing, and availability. Fetching 50 products to render 4 multiplies those queries, which increases TTFB and delays FCP and LCP.

How you avoid the extra fetch depends on the array.


Anchor to [object Object], and ,[object Object],: ,[object Object], is enoughcollection.products and collections: limit is enough

Both of these arrays reduce the fetch to match a for loop's limit, so no paginate wrapper is needed:

{%- comment -%} Fetches 4 products, not 50 {%- endcomment -%}
{% for product in collection.products limit: 4 %}
{{ product.title }}
{% endfor %}

The optimization applies when the loop isn't already inside a {% paginate %} block and the limit is below the array's default page size, which is 50 for collection.products and 1,000 for collections. Without a limit, the loop fetches the full default page size.

Caution

Don't reach for {% paginate %} to fix a one-off carousel or featured-products row. A {% paginate %} block responds to the page URL parameter, so a section that's wrapped in one renders different items, or none at all, on ?page=2. Use limit for fixed-size lists and reserve {% paginate %} for lists that a customer actually pages through.

blog.articles, search.results, product.variants, pages, customer.orders, customer.addresses, and article.comments don't reduce their fetch in response to limit. For these, limit controls iterations only, and {% paginate %} controls the fetch:

{%- comment -%} Fetches the default page size, then iterates 4 times {%- endcomment -%}
{% for article in blog.articles limit: 4 %}
{{ article.title }}
{% endfor %}

{%- comment -%} Fetches 4 articles {%- endcomment -%}
{% paginate blog.articles by 4 %}
{% for article in blog.articles limit: 4 %}
{{ article.title }}
{% endfor %}
{% endpaginate %}

Keep both paginate by 4 and limit: 4. The paginate tag limits the database query, and limit caps the iterations.


A fixed-size row of products needs limit only. Adding {% paginate %} here would couple the row to the page URL parameter:

<div class="featured-products">
{% for product in collections.featured.products limit: 4 %}
<div class="product-card">
{{ product.featured_image | image_url: width: 600 | image_tag: loading: 'lazy' }}
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</div>
{% endfor %}
</div>

blog.articles doesn't respond to limit, so this one needs the paginate wrapper:

{% paginate blog.articles by 4 %}
{% for article in blog.articles limit: 4 %}
{{ article.title }}
{% endfor %}
{% endpaginate %}

Section typeRecommended limitReasoning
Featured products4 to 6Above the fold, highly visible
Carousels (initial load)4 to 8Only show what's initially visible
Product recommendations2 to 4Most relevant only
Grid sections8 to 12Balance between content and load time
Paginated collections24 to 50Per-page limit with pagination controls

  • Use the Theme Inspector to compare Liquid render time with different product limits.
  • Measure TTFB in the Chrome DevTools Network panel with different query sizes.
  • Check the server-timing header for database query counts.


Was this page helpful?