Hello! Welcome back to our module on Advanced Theming & Performance Optimization.
In our last lesson, we focused on optimizing assets, specifically how to load images efficiently using loading="lazy", fetchpriority="high", and the powerful image_tag filter. We established that asset loading is a major factor in theme performance.
Today, we shift our focus from the assets themselves to the Liquid code that generates the page structure. You'll learn a fundamental best practice for modern Shopify theme development: how to use the Liquid render tag and why it offers significant improvements in performance and maintainability over the older, deprecated include tag.
This concept is crucial. Much like how component-based frameworks like React enforce a clean data flow with props, the render tag brings a more disciplined and performant approach to building reusable UI components in Liquid. Your experience with React will make the core principle behind render feel very familiar.
1. The Evolution of Reusable Code: From include to render
At the heart of any scalable frontend architecture is the principle of "Don't Repeat Yourself" (DRY). In Shopify themes, this is achieved using snippets—small, reusable pieces of Liquid code stored in the /snippets directory.
To start, let's get a clear understanding of what snippets are and why they are essential.
How to Add Snippets in Shopify in Just 3 Steps (2025 Guide)
This article, 'How to Add Snippets in Shopify', provides an excellent overview of what snippets are and their importance. It also draws a direct comparison to React components, which will be helpful.
Please read the first two sections, 'What Are Snippets in Shopify?' and 'What Makes Snippets in Shopify Essential?'. Focus on the benefits of isolation, reusability, and performance.
For years, the way to use a snippet was with the {% include %} tag. However, this method had a major drawback that led to performance issues and hard-to-maintain code.
The Problem with include: Shared Scope
The include tag worked by injecting the snippet's code directly into the parent template, inheriting the parent's entire variable scope.
Imagine this scenario:
templates/collection.liquiddefines a variable:{% assign layout_style = 'grid' %}.- It then loops through products:
{% for product in collection.products %}. - Inside the loop, it calls
{% include 'product-card' %}.
The snippets/product-card.liquid file would automatically have access to layout_style and the current product object from the loop, without them being explicitly passed. While convenient at first glance, this "magic" access leads to problems:
- Scope Pollution: The snippet could accidentally modify a variable from the parent template, causing unpredictable side effects elsewhere on the page.
- Poor Readability: It's impossible to know what data a snippet needs just by looking at the
{% include 'product-card' %}tag. You have to analyze the parent template to understand its dependencies. - Performance Bottlenecks: The snippet has access to everything in the parent scope, even large, complex objects it doesn't need. In loops, this can slow down rendering as the entire scope is carried into every iteration.
This is why Shopify deprecated include. The modern, preferred method is the render tag.
2. The render Tag: Enforcing Clean and Performant Code
The render tag was introduced to solve the problems of include. Its single most important feature is scope isolation.
A snippet called with render does not inherit the parent's scope. It runs in its own isolated environment.
This is precisely analogous to props in React. A child component doesn't have access to its parent's state; data must be explicitly passed down. This creates a predictable and one-way data flow, making the system easier to reason about, debug, and optimize.
To understand the render tag and its syntax, the official Shopify documentation is the best source.
The official documentation for the render tag explains the isolated scope and details the correct syntax for passing data into a snippet.
Read the introductory paragraphs before the 'Syntax' heading. Then, skip to the 'render tag parameters' section and review the syntax for 'Passing variables to a snippet', 'for', and 'with'.
As the documentation highlights, this isolated scope means you must explicitly pass any data the snippet needs as parameters. Let's see how this works.
Passing Data with render
Let's rebuild our product-card example the modern way.
snippets/product-card.liquid
This file expects a variable named card_product to be available. Note that we can still access global objects like settings.
{% comment %}
This snippet requires a 'card_product' object to be passed in.
{% endcomment %}
<div class="product-card">
<a href="{{ card_product.url }}">
<img
src="{{ card_product.featured_image | image_url: width: 400 }}"
width="{{ card_product.featured_image.width }}"
height="{{ card_product.featured_image.height }}"
loading="lazy"
alt="{{ card_product.featured_image.alt | escape }}"
>
<h3>{{ card_product.title }}</h3>
<p>{{ card_product.price | money }}</p>
</a>
</div>
templates/collection.liquid
Now, we use render to call the snippet inside our loop, explicitly passing the product object.
{% comment %}
Loop through products and render the product-card snippet for each one,
passing the 'product' object from the loop as the 'card_product' variable.
{% endcomment %}
{% for product in collection.products %}
{% render 'product-card', card_product: product %}
{% endfor %}
Shopify also provides a highly optimized syntax for rendering snippets within a loop using the for parameter:
{% comment %} A more concise and performant way to do the same thing {% endcomment %}
{% render 'product-card', for: collection.products as card_product %}
In this syntax, Shopify automatically loops through collection.products and passes each item into the snippet as the card_product variable. This is the recommended approach for rendering collections.
The Performance Gain
Why is render faster?
- Reduced Overhead: Since the snippet only receives the data it's explicitly given, the Liquid interpreter doesn't need to carry the entire parent scope into the snippet's execution context. This is especially impactful inside loops.
- Better Caching: Shopify can more effectively cache the output of rendered snippets because their dependencies are explicit and isolated. If the input parameters haven't changed, the cached output can often be reused.
This video quickly summarizes the transition from include to render.
Shopify Theme Development - Liquid Theme Architecture
This clip from the 'Stacking Context' channel clearly states that include is deprecated and render is the modern standard, reinforcing the 'why'.
Watch from 02:42 to 03:45. The presenter introduces snippets and directly addresses the deprecation of include in favor of render.
The table in the article you read earlier perfectly summarizes the differences. It's a great reference to keep in mind.
A table comparing the features of the render and include tags
This table from the Enstacked article clearly contrasts render and include on scope isolation, performance, and support status, highlighting why render is the superior choice.
Test your understanding!
You are creating a snippet named icon.liquid that needs to display an SVG icon. You want to be able to control the icon's color and size from the parent template.
The snippet snippets/icon.liquid looks like this:
<svg width="{{ size | default: 16 }}" height="{{ size | default: 16 }}" fill="{{ color | default: 'currentColor' }}">
...
</svg>
How would you call this snippet from a template to render a red icon with a size of 24px?
Show answer
You would use the render tag and pass the color and size as named parameters:
{% render 'icon', color: 'red', size: 24 %}
This demonstrates the core principle of explicitly passing data into the snippet's isolated scope.
Conclusion
Adopting the render tag isn't just about following a new syntax; it's about embracing a more robust, maintainable, and performant way of building Shopify themes. It forces a clean separation of concerns and makes your theme's data flow transparent and predictable.
Key Takeaways:
- Always use
render, neverinclude: Theincludetag is deprecated due to performance and maintainability issues caused by its shared scope. - Embrace Isolated Scope:
rendercreates a private scope for each snippet. All data must be passed in explicitly as parameters, similar to passing props in React. - Explicit is Better than Implicit: This pattern makes your code easier to debug and reason about, as a snippet's dependencies are declared at the point where it's called.
- Performance by Design: The isolated scope allows Shopify to optimize rendering, especially in loops, leading to faster page loads. Use the
{% render 'snippet', for: array as item %}syntax for the best performance when iterating over collections.
In our last two lessons, we've zeroed in on specific, high-impact optimization techniques for images and Liquid code. In our next and final lesson for this module, we will zoom out and learn how to analyze theme performance using Shopify's built-in tools and browser dev tools, allowing you to identify the very bottlenecks that these techniques are designed to solve.