{"title":"Liquid tag options","slug":"liquid-tag-options","url":"https://support.storeconnect.com/articles/liquid-tag-options","url_markdown":"https://support.storeconnect.com/articles/liquid-tag-options.md","subtitle":null,"summary":"The option syntax every StoreConnect Liquid tag shares, and the v22 change that stops a tag option value taken from a variable being executed as Liquid. Covers how to spot an affected template, the two ways to rewrite it, and the tenant setting that restores the old behavior along with its security cost.","type":"Developer_Documentation","video_url":"","keywords":"liquid tag options, tag option values, option syntax, key value pairs, markup in options, liquid_interpolate_tag_option_values, SC_LIQUID_INTERPOLATE_TAG_OPTION_VALUES, breaking change, v22, capture, render tag, liquid console, translation key, theme upgrade","last_modified":"2026-10-20T05:27:39+0000","body_markdown":"Most StoreConnect Liquid tags take options: comma-separated `key: value` pairs written after the tag name. This article covers the option syntax they all share, and a change in v22 that affects any theme passing stored text into a tag option.\n\n## Option syntax\n\n\n```liquid\n\n{% render \"product-card\", label: \"Add to cart\", product: product, featured: true %}\n```\n\n\nAn option value can be a quoted string, a variable, `true` or `false`, or a list:\n\n\n```liquid\n\n{% render \"gallery\", items: [1, \"2\", variable] %}\n```\n\n\nA string starting with `t.` is looked up as a translation key rather than printed:\n\n\n```liquid\n\n{% render \"product-card\", label: \"t.products.add_to_cart\" %}\n```\n\n\n## Markup in an option value\n\nLiquid markup written directly in an option value is rendered:\n\n\n```liquid\n\n{% render \"greeting\", label: \"Hi {{ current_customer.firstname }}\" %}\n```\n\n\nMarkup renders only where you write it in the template. A variable is read for its value, and that value is treated as text, never as a template to run.\n\n:::warning\nThis is a change in v22, and it is the one breaking change v22 asks theme developers to act on. Before v22, a value arriving from a variable was executed as Liquid. A theme that passes stored markup into a tag option renders differently from v22 onward.\n:::\n\n### What no longer works\n\nSay a merchant types `Save {{ discount }}%` into a custom text field on the product, and the theme passes that field to a tag:\n\n\n```liquid\n\n{% render \"badge\", label: product.data['Badge_Text__c'] %}\n```\n\n\nFrom v22 the badge shows the literal text `Save {{ discount }}%`, braces and all. The stored markup is data, so it is printed rather than executed.\n\n### How to find affected templates\n\nWhenever an option value's text contains `{{` or `{%` and is printed rather than executed, an error appears in the Liquid Console naming the option and showing the value. Work through your templates with the console open rather than reading them by eye. See [Debugging and performance](liquid-debugging-and-performance).\n\nEach option and value is reported once, so a tag inside a loop over many records gives you one error rather than one per record. A page with several affected tags still reports each of them.\n\n### Two ways to rewrite it\n\nWrite the markup in the template, where you can see it:\n\n\n```liquid\n\n{% assign discount = 20 %}\n{% render \"badge\", label: \"Save {{ discount }}%\" %}\n```\n\n\nTo combine a merchant's own text with markup, build the value first and pass the result:\n\n\n```liquid\n\n{% assign discount = 20 %}\n{% capture badge_label %}{{ product.data['Badge_Text__c'] }} - save {{ discount }}%{% endcapture %}\n{% render \"badge\", label: badge_label %}\n```\n\n\nA capture holds text, so this puts the markup where you can see it. Note that a capture is a way to build a value, not a way to opt out of the change: a captured value, an assigned value, and a field read straight from a record are all treated the same way.\n\nThe same rule applies to `{% update %}`, which takes its options through the same parser.\n\n## The setting that restores the old behavior\n\nA tenant can be set to execute markup that arrives from a variable, through the `liquid_interpolate_tag_option_values` tenant setting, or the `SC_LIQUID_INTERPOLATE_TAG_OPTION_VALUES` environment variable for every tenant that has not set its own value. A tenant's own setting wins, so a tenant that sets it to `false` stays off even where the environment variable turns it on.\n\nNeither is a store setting. The setting applies to every store in the tenant, and it cannot be turned on for one store and left off for another. StoreConnect holds this configuration, so ask support if you need it changed.\n\n:::warning\nTurning this on is not recommended, and it is not only a formatting change. With the setting on, text a merchant or a customer has entered is executed as Liquid wherever a theme passes it to a tag option. Anyone who can type into a product description, a custom field, or a form submission can run template code on your storefront, with the same reach any theme code has: it can read records with `{% query %}` and make outbound HTTP requests with `{% api %}`. One string typed into a field can read customer records and send them to an address outside your store. HTML escaping does not prevent this, because it does not escape Liquid's braces.\n\nTreat it as a short-term measure while templates are updated, and have it turned off again afterward.\n:::\n\nMarkup you write in your own templates is unaffected either way, and so is printing a value with `{{ ... }}`. That text is printed, never executed, whichever way the setting is set. The setting changes only what happens to a value passed to a tag option."}