How to Use Shopify Metaobjects as Global Variables
I recently worked on a Shopify store that required displaying product prices only during valid sales periods, defined by start and end dates. To achieve this without configuring dates for each product manually, I used Shopify Metaobjects.
Metaobjects are not natively accessible globally across a theme. They require explicit linking through the Theme Customizer or individual sections, which complicates reusable logic. In this post, I’ll share how I solved this by dynamically linking a metaobject to the Theme Customizer and making its data globally accessible with Liquid and JavaScript.
Problem Statement
The Goal:
- Dynamically fetch and use a Metaobject entry (
sales-dates) globally across the theme. - Display product prices only during valid sales periods (start and end dates defined in the metaobject).
- Allow store admins to easily update the sales dates from the Metaobject by modifying the Sale’s Dates entry.
Challenges Identified:
- Scoped Metaobject Access:
Metaobjects are not globally accessible; they are scoped to specific sections or snippets where they are explicitly linked. - Dynamic Linking:
Hardcoding handles likesales-dates-entryfailed because Shopify’s Liquid couldn’t reliably resolve metaobject fields without proper schema linkage.
Final Solution: Using Theme Settings for Global Metaobject Access
The solution leverages the Theme Customizer’s settings_schema.json to dynamically link a Metaobject entry and make its data available globally through Liquid and JavaScript.
Here’s how I did it:
Step 1: Define the Metaobject in settings_schema.json
First, I declared the metaobject in settings_schema.json to make it accessible in the Theme Customizer. This allows store admins to select the sales date metaobject dynamically.
{
"name": "Global Sales Dates",
"settings": [
{
"type": "metaobject",
"id": "sales_dates",
"label": "Sales Dates",
"metaobject_type": "sales_dates"
}
]
}Explanation:
metaobject_type: This specifies the type of metaobject (in this case,sales_dates).- Dynamic Linking: The admin can select the
sales_datesentry directly in the Theme Customizer.
This step ensures Shopify recognizes the metaobject entry and links it to the theme.
Step 2: Centralizing Global Variables in theme.liquid
In theme.liquid, I fetched the selected metaobject entry dynamically and processed its fields (e.g., start and end dates). Then I created global availability by making window.salesSettings object accessible throughout the entire theme, making it easy to apply sales logic across multiple sections.
The Liquid logic for fetching Metaobject values runs server-side in theme.liquid, minimizing client-side computations.
Variables become globally accessible across the entire theme because theme.liquid is the wrapper for every page.
<!-- Setting Up the Sale Global Variables -->
{%- assign selected_handle = settings.sales_dates -%}
{%- if selected_handle != blank -%}
{%- assign sales_dates = shop.metaobjects.sales_dates[selected_handle] -%}
{% if sales_dates %}
{%- assign current_date = "now" | date: "%Y-%m-%d" -%}
{%- assign start_date = sales_dates.start -%}
{%- assign end_date = sales_dates.end -%}
{% if current_date >= start_date and current_date <= end_date %}
{%- assign sale_available = true -%}
{% else %}
{%- assign sale_available = false -%}
{% endif %}
<!-- Store sale settings globally -->
<script>
window.salesSettings = {
current_date: "{{ current_date }}",
start_date: "{{ start_date }}",
end_date: "{{ end_date }}",
saleAvailable: "{{ sale_available }}",
};
</script>
{% endif %}
{%- endif %}Step 3: Creating and including a JavaScript file to store variables
I included the .js file in the theme, just after the Setting the Sale’s Global Variables above.
Using defer ensures the JavaScript file is loaded after the DOM is parsed, improving overall page performance.
<!-- Get Global Sales Dates Snippet -->
<script defer src="{{ 'global-sales.js' | asset_url }}"></script>Step 4: Storing Variables in JavaScript for Global Logic
With the sales dates injected into window.salesDates, I used JavaScript to check if the current date was within the sales period and apply logic globally.
document.addEventListener("DOMContentLoaded", function () {
if (window.salesDates) {
const currentDate = new Date(window.salesDates.current_date);
const startDate = new Date(window.salesDates.start_date);
const endDate = new Date(window.salesDates.end_date);
const isSaleActive =
currentDate >= startDate && currentDate <= endDate;
// Add a global class for sale state
document.body.classList.toggle("sale-active", isSaleActive);
// Example: Hide product prices outside sale period
document.querySelectorAll(".product-grid--price").forEach((price) => {
price.style.display = isSaleActive ? "block" : "none";
});
}
});Explanation:
- Sale Logic: Checks if the current date falls within the sales period.
- Global Styling: Adds a
sale-activeclass to thebodytag for universal styling. - Dynamic Content: Example logic hides or displays product prices based on the sales status.
Step 5: Using the Global Variable in Liquid Templates
The saleAvailable variable, declared in theme.liquid, is now accessible throughout the theme. Here’s an example of how I used it to conditionally display content based on the sales state:
{% if sale_available %}
<!-- Show content for active sales -->
<div class="sale-products">
<h2>Our Sale Products</h2>
<p>Check out the amazing discounts during our sale period!</p>
<!-- Add logic to loop through and display sale products -->
</div>
{% else %}
<!-- Fallback content for inactive sales -->
<div class="no-sale">
<h2>No Sale Active</h2>
<p>Stay tuned for our next amazing sale!</p>
</div>
{% endif %}Benefits of the Solution
- Dynamic Global Access:
The metaobject entry is dynamically fetched and injected intowindow.salesDates, making it available globally. - Reusable Logic:
Centralizing the code in a snippet ensures consistent behavior and reduces redundancy. - Theme Customizer Integration:
Store admins can easily update or change the metaobject entry without editing code. - Scalability:
This approach works for additional fields (e.g., promotional messages, banners) with minimal changes.
Why This Solution Works (and What Didn’t Work Before)
To fully appreciate why this solution works, let’s first look at the limitations of the earlier approaches and the challenges I encountered. While working with Shopify metaobjects, I tried several initial methods that seemed viable but ultimately fell short.
Previous Attempts and Their Shortcomings
- Hardcoding Metaobject Handles in Liquid
- Attempt: I initially hardcoded the handle of the metaobject (e.g.,
sales-dates-entry) into the Liquid template and tried to fetch its data directly:
- Attempt: I initially hardcoded the handle of the metaobject (e.g.,
{%- assign sales_dates = shop.metaobjects.sales_dates["sales-dates-entry"] -%}- Problem: Shopify Liquid couldn’t reliably resolve the metaobject fields. Without dynamic linkage through
settings_schema.json, Shopify does not recognize or validate hardcoded metaobject handles, leading to errors ornullvalues. - Why It Failed: Metaobjects in Shopify require explicit linking to the theme schema or settings for proper resolution. Hardcoding bypasses this mechanism, making it unreliable.
Fetching Global Metaobjects Without Schema Integration
- Attempt: I tried using Shopify’s Liquid to fetch a metaobject globally without declaring it in
settings_schema.json, assuming it could be referenced directly.liquidCopy code
{%- assign sales_dates = shop.metaobjects.sales_dates[some_handle] -%}- Problem: Shopify Liquid does not support dynamically fetching metaobjects without them being explicitly linked to the schema. This approach failed because Shopify had no context for which metaobject to fetch.
- Why It Failed: Shopify restricts access to metaobjects unless they are explicitly declared in the theme settings. This ensures that the system validates and scopes metaobject entries appropriately.
Duplication of Logic in Multiple Sections
- Attempt: I considered fetching and processing the metaobject data separately in each section or snippet where it was needed.liquid
{%- assign sales_dates = shop.metaobjects.sales_dates[section_handle] -%}- Problem: This introduced redundancy, as the same logic had to be repeated across multiple files, making the theme harder to maintain and update.
- Why It Failed: While this approach worked technically, it was inefficient and prone to errors, as any change in logic required updating multiple files.
Why the Final Solution Works
- Dynamic Metaobject Linking
- The key to this solution is declaring the metaobject in
settings_schema.json. By adding a metaobject type and allowing the store admin to select the desired entry in the Theme Customizer, Shopify dynamically resolves the correct handle. This eliminates the need for hardcoding and ensures that the data is always valid and up-to-date.
- The key to this solution is declaring the metaobject in
- Global JavaScript Integration
- Injecting the metaobject data into
window.salesDatesprovides a flexible way to use the data globally across the theme. This approach bridges the gap between server-side Liquid and client-side JavaScript, enabling dynamic, interactive functionality like date-based logic for sales.
- Injecting the metaobject data into
- Theme Customizer Integration
- By integrating the metaobject selection into the Theme Customizer, the solution gives store admins the ability to update or switch metaobject entries without editing code. This improves usability and ensures that the theme remains flexible for non-technical users.
Summary of the Key Differences
| Previous Attempts | Why They Failed | Final Solution | Why It Works |
|---|---|---|---|
| Hardcoding metaobject handles | Shopify Liquid cannot reliably resolve hardcoded handles without schema linkage. | Dynamic linking via settings_schema.json. | Ensures Shopify recognizes and validates the selected metaobject. |
| Fetching global metaobjects directly | Shopify restricts global metaobject access without proper schema integration. | Declares metaobject in settings and fetches it dynamically in theme.liquid. | Provides a valid, scoped context for metaobject resolution. |
| Duplicating logic in multiple files | Inefficient and error-prone; requires repetitive updates across sections/snippets. | Centralized logic in a reusable snippet (global-sales-dates.liquid). | Ensures consistency, reduces redundancy, and simplifies maintenance. |
| No JavaScript integration | Limited to server-side rendering; lacks interactivity or dynamic updates. | Injects metaobject data into window.salesDates for use in client-side logic. | Enables dynamic, global interactions like toggling sales states in real time. |
Final Thoughts
The key insight that made the solution work was understanding that Shopify metaobjects require dynamic linkage through settings_schema.json to function properly. By leveraging the Theme Customizer, centralizing logic in a snippet, and injecting data into JavaScript, this solution combines flexibility, scalability, and usability, making it the perfect approach for globally accessible metaobject logic.
If you’re tackling similar challenges, remember to focus on dynamic schema integration, reusable code, and seamless admin workflows for the best results!
Best Practices
- Always Use Dynamic Linking:
Declare metaobjects insettings_schema.jsonto ensure Shopify dynamically resolves and validates the selected entry. - Centralize Logic:
Use snippets for shared logic to avoid duplication and ensure consistent behavior. - Pass Data to JavaScript:
Inject server-rendered data intowindowwhen interactive features depend on metaobject values.
Valuable Resources
If you’re looking to create global variables using Shopify metaobjects, this solution offers a flexible and scalable approach. It combines dynamic metaobject linking with reusable logic and JavaScript for seamless integration. Try it on your Shopify store today, and share your experience in the comments below. For more insights, explore Shopify’s metaobject documentation.