Skip to Content
Custom Liquid

Custom Liquid

When a Mercer section doesn’t fit your needs, the Custom Liquid section and the Custom Liquid block let you drop in arbitrary Liquid, HTML, and inline style markup. This guide shows where each lives, when to use which, and what’s safe (and unsafe) to put inside.

Before adding custom code, duplicate your theme in Shopify admin. Standard Mercer support covers documented theme behavior; developing or maintaining your own Custom Liquid is outside that scope. A modified theme still qualifies for theme support, though we may ask you to reproduce an issue in a clean, unpublished draft. For advanced customization, a Shopify Partner can help. See the support policy.

Mercer ships two Custom Liquid surfaces:

  • Custom Liquid section — a free-form section you can add anywhere a section is allowed (every template except the gift-card page, which isn’t section-based). It can be added to the password page.
  • Custom Liquid block — available inside every section that supports @app blocks. Use this when you want to inline custom markup inside an existing section instead of as its own row on the page.

If you’re new to Liquid, see Shopify’s Liquid reference .

Custom Liquid section

The section has one setting: a Liquid code textarea. Whatever you put in it is rendered as-is — Liquid evaluates, HTML renders, inline style and script tags work.

To add it:

  1. Open the Theme Editor.
  2. Pick the template you want to add it to.
  3. Click Add section → Custom Liquid.
  4. Click into the section, paste your Liquid / HTML.
  5. Save.

Example: announcement strip

<div style="background: #FFD7BA; color: #4A1F00; text-align: center; padding: 0.5rem;"> Free shipping on orders over $80 — code <strong>SUMMER</strong>. </div>

Example: time-gated banner (Liquid if + now)

{%- assign launch = '2026-06-01' | date: '%s' -%} {%- assign now = 'now' | date: '%s' -%} {%- if now < launch -%} <div style="background: #1A1A1A; color: #fff; text-align: center; padding: 1rem;"> The Spring 2026 collection drops June 1. </div> {%- endif -%}

Example: pulling a metafield value

{%- assign tagline = shop.metafields.brand.tagline -%} {%- if tagline -%} <p class="brand-tagline">{{ tagline }}</p> {%- endif -%}

Example: third-party embed

<!-- Press testimonial widget --> <div id="press-widget" data-widget="press"></div> <script src="https://example.com/widget.js" defer></script>

Custom Liquid block

Available inside any section that has a “Blocks” panel and supports @app blocks. To add:

  1. Open a section in the Editor (e.g. Image with text).
  2. Open the section’s block list in the editor sidebar.
  3. Click Add block → Custom Liquid.
  4. Drag to reorder among the section’s other blocks.
  5. Click into the block, paste your Liquid / HTML.

The block lets you compose custom content inside an existing section without breaking the section’s layout. Common pattern: appending a note below an image-with-text block, or inserting a custom “Why we built this” paragraph between two columns of a multi-column section.

When to use which

If you want…Use the…
A whole-row, full-width custom sliceCustom Liquid section
Custom markup inside an existing section’s flowCustom Liquid block
To embed a third-party app without codeThe third-party app’s app block
To show different content per market / localeA Custom Liquid section with if request.locale.iso_code == 'en'
To override Mercer’s PDP layoutOpen the product template and edit its Product section; don’t fight it with Custom Liquid

Available Liquid drops and filters

Inside Custom Liquid, you have access to all of Shopify’s standard global drops:

  • shop — store name, currency, etc.
  • request — the current request (request.locale, request.path).
  • customer — the signed-in customer (or nil).
  • cart — the current cart.
  • linklists.<handle> — your menu lists.
  • routes — Shopify route helpers (routes.cart_url, etc.).
  • localization — the active country / language (localization.country, localization.language).
  • template — the current template name.
  • page_title, page_description — current page meta.

Note: Mercer.routes is not a Liquid drop — it’s a client-side JavaScript global (window.Mercer, defined in the theme’s JS). It isn’t accessible in Liquid; {{ Mercer.routes }} renders nothing. Use it from an inline <script> for locale-aware AJAX, e.g. Mercer.routes.url('cart/add.js').

Localization in Custom Liquid

If you hard-code a path like /products/wool-trench, that path will not be auto-localized for visitors in other markets. To localize correctly, use routes or the localized-href snippet:

<!-- Bad: hard-coded path doesn't localize --> <a href="/products/wool-trench">Shop the trench</a> <!-- Good: routes-prefixed --> <a href="{{ routes.collections_url }}/outerwear">Shop outerwear</a> <!-- Best: explicit collection drop --> {%- assign coll = collections.outerwear -%} {%- if coll -%} <a href="{{ coll.url }}">{{ coll.title }}</a> {%- endif -%}

For internal links to products / collections / pages, prefer the Shopify drops (product.url, collection.url, page.url) — Shopify auto-localizes these drop URLs for the active market. For raw paths that don’t come from a drop, the theme prefixes them with routes.root_url (see the localized-href snippet).

Custom CSS

Inline style tags work inside Custom Liquid. For one-off styling, this is fine. For anything cross-page or theme-wide, you’re better off asking the Mercer team to add a setting that exposes the value you need (via support) — Custom Liquid CSS isn’t easily reusable.

<style> .my-custom-banner { background: var(--surface-alt); color: var(--ink); padding: var(--space-3); } </style> <div class="my-custom-banner"> Custom announcement using Mercer's design tokens. </div>

Mercer’s design tokens (the var(--ink), var(--surface), var(--space-3) etc. above) are available globally. Using them keeps your custom block visually consistent with the rest of the storefront.

Custom JavaScript

An inline classic script runs when the browser parses it, so later HTML and Mercer’s deferred scripts may not be ready. defer only delays external classic scripts with a src attribute; it has no effect on an inline classic script. See the script element reference .

For a small initializer, check readiness before attaching the listener:

<script> (() => { function initialize() { // Find this widget's element and initialize it once. } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize, { once: true }); } else { initialize(); } })(); </script>

The theme editor can replace section HTML without a full page load. Scripts inserted through innerHTML do not execute automatically, and DOMContentLoaded does not fire again. For reusable widgets, put initialization in a theme asset loaded once, make it safe to run more than once, and handle shopify:section:load and cleanup events. Do not depend on private theme globals being available immediately.

Security and what NOT to put in Custom Liquid

Custom Liquid runs as the merchant’s code. It’s not sandboxed. Some things to avoid:

  • Don’t paste customer-submitted content without filtering. Shopify form submissions (cart notes, customer names, etc.) can contain HTML; rendering them raw is an XSS vector. If you absolutely must render them, append | escape: {{ form.name | escape }} not {{ form.name }}.
  • Don’t paste credentials. Custom Liquid renders to public HTML. API keys, tokens, passwords are leaked to anyone who views the page source.
  • Don’t disable Mercer’s script defers. Mercer’s perf budget depends on all main-bundle scripts being deferred. If your custom script blocks render, your LCP and CLS metrics suffer.

If a third-party widget you want to embed asks you to inject a script tag without defer, ask the vendor for a deferred build. If they don’t have one, that’s a perf cost you’re choosing to pay; it won’t fall on Mercer.

Editor preview vs production rendering

The theme editor can re-render sections without reloading the preview. Use localization.country for the selected storefront country; it is not a promise of the visitor’s physical location. The request object has no country property.

Customer and cart state depend on the preview session. Do not assume that the customer is always absent or the cart is always empty. Test customer-aware and cart-aware code on a storefront preview with representative session states.

Common Custom Liquid pitfalls

”My Custom Liquid breaks the page”

A syntax error in Liquid can break the entire page render. Check the error reported in the editor or storefront preview and review the Liquid around the reported location. Error presentation can vary.

If the page won’t render at all, save an empty Custom Liquid first to restore the section, then debug your snippet in a smaller scope.

”My script runs twice”

Most likely you’ve added a script tag to a Custom Liquid block inside a section, and that section appears more than once on the page. Each section instance renders its own copy of the block. Solutions:

  • Wrap the script in a “has it run yet” guard: if (!window.__myWidget) { window.__myWidget = true; /* initialize */ }
  • Move the script to a Custom Liquid section instead of a block, and add it to the page once.

”My styles don’t apply”

Inline style tags work, but specificity issues can mean Mercer’s own CSS overrides yours. Use the Mercer design tokens (var(--ink), var(--space-3)) to compose with the theme; or scope your selectors with a unique class (e.g. .my-banner-XYZ) and avoid using !important.

”My CTA button doesn’t look like Mercer’s other buttons”

Use Mercer’s button class:

<a class="btn btn--primary" href="...">Solid CTA</a> <a class="btn btn--secondary" href="...">Outlined / secondary CTA</a> <a class="btn btn--ghost" href="...">Lighter / ghost CTA</a>

The button styles inherit your preset’s radius / border / letter-spacing settings. Hand-coded buttons won’t.

What’s next

  • Read Section reference to see if a built-in section can replace your Custom Liquid use case.
  • Read the FAQ for general theme gotchas.
  • For widgets / sections you’d like to see built into Mercer natively, use the support form and choose Feature request. Suggestions do not guarantee a feature or delivery date.