Liquid Variables Don't Work Inside {% javascript %} Tags — Here's Why and the Fix
You write {% javascript %}const productId = {{ product.id }};{% endjavascript %} inside a Shopify section. The JavaScript runs but productId is undefined. Or worse, the entire script block errors. Liquid variables are not processed inside {% javascript %} tags — this is intentional. The {% javascript %} tag is for bundleable JavaScript that gets processed and cached independently of any Liquid context. This guide explains why, and gives you the correct patterns to bridge Liquid data into JavaScript.
Why Liquid does not render inside {% javascript %}
Shopify's {% javascript %} tag was introduced for section JavaScript bundling — Shopify concatenates all {% javascript %} blocks from all sections on a page into a single cached JavaScript bundle. This bundle is served from Shopify's CDN and cached aggressively. If Liquid variables were processed inside {% javascript %}, every unique combination of Liquid values would produce a different bundle — making caching impossible. The tag deliberately treats its content as static JavaScript text.
The correct pattern: data attributes on the section element
Pass Liquid values to JavaScript via data attributes on the section's root element or a specific element within it: <div id="product-{{ section.id }}" data-product-id="{{ product.id }}" data-product-handle="{{ product.handle }}" data-price="{{ product.selected_or_first_available_variant.price }}" data-section-id="{{ section.id }}">. In JavaScript, read these attributes: const container = document.querySelector('#product-{{ section.id }}'); const productId = container.dataset.productId;. This pattern works with JavaScript bundling and keeps Liquid data and JavaScript logic cleanly separated.