Skip to main content

Cache block.settings in a variable

Assign block.settings to a variable instead of accessing it repeatedly in Liquid.


There's a performance cost to accessing the block.settings object each time it appears in a template. Liquid must resolve the property chain (block, then settings, then the specific key) on every access. We're working to improve this, but caching it in a variable with assign is a reliable optimization today.

The cost multiplies inside loops. Consider a section with 20 blocks, each referencing 5 settings properties. That's 20 × 5 = 100 block.settings lookups per page render. Assigning block.settings to a variable at the top of each iteration changes the math: 20 assign operations and 100 fast variable reads. Variable reads are cheaper than the full property-chain resolution.

This optimization applies equally to section.settings when a section accesses its own settings many times.


At the top of a block loop iteration, or at the top of a section that accesses section.settings many times, assign the settings object to a descriptive variable and reference that variable throughout:

{%- assign block_settings = block.settings -%}

Anchor to Block settings accessed multiple timesBlock settings accessed multiple times

Less performant:

<h2>{{ block.settings.title }}</h2>
<p>{{ block.settings.text }}</p>
<a href="{{ block.settings.link_url }}">{{ block.settings.link_text }}</a>
{% if block.settings.show_image and block.settings.image != blank %}
{{ block.settings.image | image_url: width: 600 | image_tag: loading: 'lazy' }}
{% endif %}

More performant:

{%- assign block_settings = block.settings -%}
<h2>{{ block_settings.title }}</h2>
<p>{{ block_settings.text }}</p>
<a href="{{ block_settings.link_url }}">{{ block_settings.link_text }}</a>
{% if block_settings.show_image and block_settings.image != blank %}
{{ block_settings.image | image_url: width: 600 | image_tag: loading: 'lazy' }}
{% endif %}

Anchor to Inside a for loop iterating over section.blocksInside a for loop iterating over section.blocks

This is where the optimization has the most impact. Each iteration avoids repeated property-chain resolution:

{%- for block in section.blocks -%}
{%- assign block_settings = block.settings -%}
<div class="slide" {{ block.shopify_attributes }}>
{% if block_settings.image != blank %}
{{ block_settings.image
| image_url: width: 800
| image_tag:
loading: 'lazy',
widths: '400, 600, 800',
sizes: '(min-width: 768px) 50vw, 100vw',
alt: block_settings.image_alt
}}
{% endif %}

<div class="slide__content">
{% if block_settings.heading != blank %}
<h2>{{ block_settings.heading }}</h2>
{% endif %}

{% if block_settings.subheading != blank %}
<p>{{ block_settings.subheading }}</p>
{% endif %}

{% if block_settings.button_label != blank %}
<a href="{{ block_settings.button_url }}" class="button">
{{ block_settings.button_label }}
</a>
{% endif %}
</div>
</div>
{%- endfor -%}

In this example, each iteration accesses block settings 9 times. With 20 blocks, that's 180 accesses. The assign at the top of each iteration turns those into 20 assigns and 180 fast variable reads instead of 180 full property-chain lookups.

Anchor to Section settings accessed many timesSection settings accessed many times

The same pattern applies to section.settings when a section accesses its own settings repeatedly:

{%- assign section_settings = section.settings -%}

<section class="hero {% if section_settings.full_width %}hero--full{% endif %}">
{% if section_settings.image != blank %}
{{ section_settings.image
| image_url: width: 1600
| image_tag:
loading: 'eager',
fetchpriority: 'high',
widths: '600, 900, 1200, 1600',
sizes: '100vw'
}}
{% endif %}
<h1>{{ section_settings.heading }}</h1>
<p>{{ section_settings.subheading }}</p>
{% if section_settings.button_label != blank %}
<a href="{{ section_settings.button_url }}" class="button">{{ section_settings.button_label }}</a>
{% endif %}
</section>

  • Use the Theme Inspector Chrome extension to measure per-section and per-block Liquid rendering time. Compare timings before and after applying the pattern.
  • Compare TTFB in the Chrome DevTools Network panel before and after applying this pattern. Filter to the document request and check the Waiting for server response time.
  • The impact is most visible on pages with many sections or blocks: homepages with slideshow sections, FAQ sections with many items, and any section that iterates over section.blocks.


Was this page helpful?