Flatten nested render calls, especially inside loops
Flatten or inline nested snippets to reduce the compounding execution overhead of render tags, especially inside loops.
includeinclude is deprecated, and you shouldn't use it anywhere in a theme. Unlike render, include gives the snippet access to the entire parent scope and lets it modify the caller's variables. That lack of scope isolation prevents the Liquid renderer from applying optimizations that it applies to render, so an include is slower than the equivalent render before nesting multiplies the cost at all. Replace every include with render, and pass the variables that the snippet needs as explicit arguments.
include is deprecated, and you shouldn't use it anywhere in a theme. Unlike render, include gives the snippet access to the entire parent scope and lets it modify the caller's variables. That lack of scope isolation prevents the Liquid renderer from applying optimizations that it applies to render, so an include is slower than the equivalent render before nesting multiplies the cost at all. Replace every include with render, and pass the variables that the snippet needs as explicit arguments.
Each {% render %} tag has a fixed cost: Liquid must look up the snippet file, create a new scope, pass variables into that scope, execute the snippet, and return the result. This cost is small for a single render, but it compounds when snippets are nested. A snippet that renders another snippet that renders a third creates three layers of overhead per call.
Inside a for loop, this multiplication becomes significant. If a product card snippet renders a price snippet and a badge snippet, and you have 50 products in a collection, then that's 50 × 3 = 150 render calls. Flattening the nesting to a single level reduces it to 50 render calls. Inlining the simplest snippets can reduce it further.
This overhead adds directly to server-side Liquid rendering time, increasing Time to First Byte (TTFB), which in turn delays FCP and LCP.
Anchor to Identify nesting depthIdentify nesting depth
Use the Theme Inspector Chrome extension to visualize snippet nesting. In the flame graph, look for deeply nested patterns where one snippet calls another in a chain. Nesting three or more levels deep is a sign of opportunity.
Anchor to Inline simple markupInline simple markup
If a snippet contains only a few lines of HTML with no conditional logic, then inline it directly into the parent. Reserve {% render %} for snippets that contain substantial logic or are reused across many sections.
Anchor to Flatten nested snippetsFlatten nested snippets
When a snippet renders other snippets, consider merging them into a single snippet. A product card that calls render 'product-price' and render 'product-badge' separately can often combine all three into one file.
Anchor to Use the ,[object Object], pattern to avoid duplicate resource loadingUse the skip_styles pattern to avoid duplicate resource loading
skip_styles pattern to avoid duplicate resource loadingWhen keeping a snippet as a render, such as a product card used in multiple sections, add a skip_styles parameter to prevent loading the same stylesheet on every iteration. See Reduce stylesheet count for the full pattern.
Liquid parses render arguments as name-value pairs, so a comparison like skip_styles: forloop.index > 1 binds skip_styles to forloop.index and silently drops the > 1. The snippet receives a number, and every number is truthy in Liquid, so {% unless skip_styles %} never runs and the stylesheet never loads. Assign the comparison to a variable first, then pass the variable.
Liquid parses render arguments as name-value pairs, so a comparison like skip_styles: forloop.index > 1 binds skip_styles to forloop.index and silently drops the > 1. The snippet receives a number, and every number is truthy in Liquid, so {% unless skip_styles %} never runs and the stylesheet never loads. Assign the comparison to a variable first, then pass the variable.
Anchor to ExamplesExamples
Anchor to Before: three render calls per productBefore: three render calls per product
sections/collection.liquid
snippets/product-card.liquid
snippets/product-price.liquid
With 50 products: 50 renders of product-card, each triggering 2 more renders (product-price and product-badge), for 150 total render calls.
Anchor to After: flattened to one levelAfter: flattened to one level
snippets/product-card.liquid
With 50 products: 50 total render calls instead of 150. The price and badge markup is inlined because it's simple HTML with basic conditionals.
For complex snippets with substantial logic, such as variant pickers, media galleries, and structured data markup, keeping them as separate renders is reasonable. The goal is to avoid nesting simple markup that doesn't benefit from the abstraction.
For complex snippets with substantial logic, such as variant pickers, media galleries, and structured data markup, keeping them as separate renders is reasonable. The goal is to avoid nesting simple markup that doesn't benefit from the abstraction.
Anchor to TestingTesting
- Use the Theme Inspector Chrome extension to visualize nesting depth. Look for deeply nested flame graph patterns on collection pages.
- Compare TTFB in the Chrome DevTools Network panel before and after flattening renders. Filter to the document request and check the Waiting for server response (TTFB) value.
- Focus on collection pages and other pages with loops that iterate many times, such as product grids, blog post lists, and navigation menus with many items.
Anchor to ReferencesReferences
rendertagincludetag (deprecated, don't use)fortagcollection.products- Reduce stylesheet count
- Debugging common causes for slow loading in Shopify Liquid storefronts
- Avoid deeply nested Liquid loops
- Move operations outside loops