Liquid Fundamentals for Theme Developers Intermediate 10 min read

Scope: Global Objects vs Template vs Section Settings

Understand how variable scope works in Shopify themes and when to use global objects, template objects, section settings, and theme settings.

Not all data is available everywhere in your theme. Some objects exist globally, others only in specific templates, and settings come from multiple sources. Understanding scope helps you know where to find the data you need and how to pass it around.

What is Scope?

Scope determines which variables and objects are accessible at any given point in your code. In Shopify themes, scope works at several levels:

  1. Global scope: Available everywhere in your theme
  2. Template scope: Available only in specific template types
  3. Section scope: Available within a section file
  4. Block scope: Available within a block loop
  5. Snippet scope: Isolated from the parent file (with render)

Global Objects

These objects are available in every file throughout your theme:

{{ shop.name }} {# Store information #}
{{ cart.item_count }} {# Shopping cart #}
{{ customer.first_name }} {# Logged-in customer (or nil) #}
{{ request.path }} {# Current page URL path #}
{{ settings.color_primary }} {# Theme settings #}
{{ localization.country }} {# Market/language info #}
{{ routes.cart_url }} {# Standard Shopify routes #}

The shop Object

Always available, contains store-wide information:

<footer>
<p>&copy; {{ "now" | date: "%Y" }} {{ shop.name }}</p>
<p>{{ shop.email }}</p>
<p>Currency: {{ shop.currency }}</p>
</footer>

The cart Object

Always available, even when empty:

<a href="{{ routes.cart_url }}" class="cart-icon">
Cart ({{ cart.item_count }})
</a>

The customer Object

Available but may be nil if no one is logged in:

{% if customer %}
Welcome back, {{ customer.first_name }}!
{% else %}
<a href="{{ routes.account_login_url }}">Log in</a>
{% endif %}

The request Object

Contains information about the current page request:

{{ request.host }} {# mystore.myshopify.com #}
{{ request.path }} {# /products/cool-shirt #}
{{ request.page_type }} {# product, collection, index, etc. #}
{{ request.design_mode }} {# true if in theme editor #}

Useful for conditional logic based on the current page:

{% if request.page_type == 'product' %}
{# Product-specific scripts #}
{% endif %}
{% if request.design_mode %}
{# Show placeholder content in editor #}
{% endif %}

Try it live: See how shop, cart, customer, and other global objects work in our Liquid Playground—all pre-loaded with realistic data.

Template-Specific Objects

These objects are only available in their corresponding template files:

ObjectAvailable In
productproduct.liquid, product.json
collectioncollection.liquid, collection.json
articlearticle.liquid
blogblog.liquid
pagepage.liquid
searchsearch.liquid
gift_cardgift_card.liquid
customercustomers/*.liquid templates

Template Objects in Sections

When a section is included in a template, the template’s objects become available:

{# sections/product-details.liquid #}
{# This section is used in product.json, so `product` is available #}
<h1>{{ product.title }}</h1>
<p>{{ product.description }}</p>

But if you try to use product in a section that’s on the homepage (where no product context exists), it will be nil:

{# sections/featured-product.liquid #}
{# On the homepage, there's no product context #}
{# This won't work without a section setting: #}
{{ product.title }}
{# Instead, use a section setting to let merchants pick a product: #}
{% assign featured = section.settings.product %}
{{ featured.title }}

Theme Settings vs Section Settings

Settings come from two different places with different scopes.

Theme Settings (Global)

Defined in config/settings_schema.json, accessed via settings:

{# Available everywhere #}
{{ settings.color_primary }}
{{ settings.type_body_font }}
{{ settings.show_announcement }}

Theme settings are perfect for site-wide values:

  • Brand colors
  • Typography choices
  • Global feature toggles

Section Settings (Local)

Defined in the section’s {% schema %}, accessed via section.settings:

{# Only available within that section file #}
{{ section.settings.heading }}
{{ section.settings.background_color }}
{{ section.settings.products_to_show }}

Example section with settings:

<section class="featured-collection"
style="background: {{ section.settings.background_color }};">
<h2>{{ section.settings.heading }}</h2>
{% for product in section.settings.collection.products limit: section.settings.limit %}
<div class="product-card">
{{ product.title }}
</div>
{% endfor %}
</section>
{% schema %}
{
"name": "Featured Collection",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading",
"default": "Featured Products"
},
{
"type": "color",
"id": "background_color",
"label": "Background color",
"default": "#ffffff"
},
{
"type": "collection",
"id": "collection",
"label": "Collection"
},
{
"type": "range",
"id": "limit",
"min": 2,
"max": 12,
"step": 1,
"default": 4,
"label": "Products to show"
}
]
}
{% endschema %}

Block Settings

Blocks are repeatable content areas within sections. Each block has its own settings:

{% for block in section.blocks %}
{% case block.type %}
{% when 'heading' %}
<h2 {{ block.shopify_attributes }}>
{{ block.settings.text }}
</h2>
{% when 'text' %}
<p {{ block.shopify_attributes }}>
{{ block.settings.content }}
</p>
{% when 'button' %}
<a href="{{ block.settings.link }}" {{ block.shopify_attributes }}>
{{ block.settings.label }}
</a>
{% endcase %}
{% endfor %}
{% schema %}
{
"name": "Content Section",
"blocks": [
{
"type": "heading",
"name": "Heading",
"settings": [
{
"type": "text",
"id": "text",
"label": "Heading text"
}
]
},
{
"type": "text",
"name": "Text",
"settings": [
{
"type": "richtext",
"id": "content",
"label": "Content"
}
]
},
{
"type": "button",
"name": "Button",
"settings": [
{
"type": "text",
"id": "label",
"label": "Button text"
},
{
"type": "url",
"id": "link",
"label": "Button link"
}
]
}
]
}
{% endschema %}

Key points about blocks:

  • Access block settings with block.settings.setting_id
  • Always include {{ block.shopify_attributes }} for theme editor functionality
  • The block.type tells you which block type you’re rendering

The section Object

Within any section file, you have access to the section object:

{{ section.id }} {# Unique ID for this section instance #}
{{ section.settings }} {# Section settings object #}
{{ section.blocks }} {# Array of blocks #}
{{ section.blocks.size }} {# Number of blocks #}
{{ section.index }} {# Position in template (1-based) #}

The section.id is especially useful for scoping CSS:

<style>
#shopify-section-{{ section.id }} .heading {
color: {{ section.settings.heading_color }};
}
</style>

Snippet Scope and the render Tag

When you include a snippet with render, it gets an isolated scope:

{# In a section file #}
{% assign featured = section.settings.product %}
{% render 'product-card', product: featured, show_vendor: true %}
{# snippets/product-card.liquid #}
{# Only has access to explicitly passed variables #}
<div class="product-card">
<h3>{{ product.title }}</h3>
{% if show_vendor %}
<p>{{ product.vendor }}</p>
{% endif %}
</div>
{# These would NOT work (not passed in): #}
{# {{ section.settings.heading }} #}
{# {{ featured }} #}

Why Isolated Scope Matters

  1. Predictability: Snippets only use what you explicitly pass
  2. Reusability: No hidden dependencies on parent variables
  3. Debugging: Easier to trace where values come from

The for Shorthand

When rendering a snippet for each item in an array:

{# Instead of this: #}
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
{# You can write: #}
{% render 'product-card' for collection.products as product %}

The with Shorthand

When passing a single object:

{# Instead of this: #}
{% render 'product-card', product: featured_product %}
{# You can write: #}
{% render 'product-card' with featured_product as product %}

Combining Settings: A Practical Example

Here’s how different setting scopes work together:

{# sections/product-grid.liquid #}
{# Theme settings: global brand values #}
{# Section settings: this section's configuration #}
{# Block settings: per-block customization #}
<section
class="product-grid"
style="
--brand-color: {{ settings.color_primary }};
--section-bg: {{ section.settings.background }};
"
>
<h2 style="font-family: {{ settings.type_header_font.family }};">
{{ section.settings.heading }}
</h2>
{% for product in section.settings.collection.products limit: section.settings.limit %}
{% render 'product-card',
product: product,
show_vendor: section.settings.show_vendor
%}
{% endfor %}
{# Blocks for additional content #}
{% for block in section.blocks %}
{% if block.type == 'promotion' %}
<div class="promo-banner" {{ block.shopify_attributes }}>
{{ block.settings.message }}
</div>
{% endif %}
{% endfor %}
</section>

Practice Exercise

Create a section that uses:

  1. A theme setting for the primary button color
  2. Section settings for heading text and a collection picker
  3. Block settings for promotional badges
<section class="promo-collection">
<h2>{{ section.settings.heading | default: "Shop Now" }}</h2>
<div class="product-grid">
{% for product in section.settings.collection.products limit: 4 %}
<article class="product-card">
{# Check if any block targets this product #}
{% for block in section.blocks %}
{% if block.settings.product == product %}
<span class="badge" {{ block.shopify_attributes }}>
{{ block.settings.badge_text }}
</span>
{% endif %}
{% endfor %}
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
<button style="background: {{ settings.color_primary }};">
Add to Cart
</button>
</article>
{% endfor %}
</div>
</section>
{% schema %}
{
"name": "Promo Collection",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Heading"
},
{
"type": "collection",
"id": "collection",
"label": "Collection"
}
],
"blocks": [
{
"type": "badge",
"name": "Product Badge",
"settings": [
{
"type": "product",
"id": "product",
"label": "Product"
},
{
"type": "text",
"id": "badge_text",
"label": "Badge text",
"default": "Sale"
}
]
}
]
}
{% endschema %}

Key Takeaways

  1. Global objects (shop, cart, settings) are available everywhere
  2. Template objects (product, collection) are only available in their template context
  3. Section settings (section.settings) are scoped to that section file
  4. Block settings (block.settings) are scoped to each block iteration
  5. Snippets with render have isolated scope and only access passed variables
  6. Use request.page_type to check what template context you’re in

What’s Next?

With scope understood, you’re ready to learn Control Flow and how to use conditionals and loops to build dynamic, responsive templates.

Finished this lesson?

Mark it complete to track your progress.

Discussion

Loading comments...