Storefront Embeds
Storefront embeds are per-theme overlays — floating widgets, analytics snippets, consent banners — that the merchant turns on in the theme customizer. Unlike App Scripts which load on every page automatically, embeds are off-by-default and require the merchant to enable them per theme. Use embeds when the merchant should control whether your overlay runs (e.g. a cookie banner that conflicts with their existing consent tool), or when settings differ per theme (light vs. dark variants).When to Use Embeds vs. App Scripts
| Need | Use |
|---|---|
| Per-theme on/off toggle in the customizer | Storefront embed |
| Settings (colour, position) merchant can change per theme | Storefront embed |
| Inline HTML overlay (badge, ribbon, banner) | Storefront embed |
| Pixel or chat widget that should always be on once installed | App Script |
| Sandboxed customer-event listener | Web Pixel |
| Liquid block dropped into a theme section | Storefront Block |
How Embeds Work
- Declare embeds in
app.jsonunderextensions.storefrontEmbeds. - After install, embeds appear in the theme customizer under App embeds.
- The merchant flips a toggle (and optionally edits per-theme settings).
The state is persisted to
templates → config/settings_data.jsonundercurrent.blocks["<appHandle>/<embedHandle>"]. - On every storefront render, the platform’s embed loader
injects enabled embeds into the rendered HTML according to each entry’s
target.
settings_data.json is disabled.
Merchants must opt in to each embed; nothing renders until they enable it
from the theme customizer.
Manifest
Fields
| Field | Type | Default | Description |
|---|---|---|---|
handle | string | — | Unique within your app. Combined with the app handle to form the embed key (<appHandle>/<embedHandle>). Required. |
title | string | handle | Label shown in the theme customizer’s app-embeds panel. |
target | "head" | "body" | "compliance_head" | "body" | Where the embed injects. head runs early, body ships just before </body>, compliance_head injects synchronously near the top of <head> for consent banners. |
inlineHtml | string | — | Raw HTML wrapped in a <div data-lms-app-embed="…">…</div>. Supports {{settings.<id>}} placeholders, escaped at render time. |
scriptSrc | string | — | URL appended as <script async src="…" data-config="…">. Merchant settings are serialised onto data-config as JSON. |
schema | object | — | Liquid-style schema ({ name, settings }) used by the customizer. Same input types as theme block schemas. |
enabled_on | object | — | { templates: ["index", "product"] } — embed runs only on listed page types. Use "*" for all. |
disabled_on | object | — | { templates: ["404"] } — embed runs everywhere except listed. Used when enabled_on is absent. |
appId | string | <appHandle> | Surfaced on the injected tag as data-app-id. |
inlineHtml and scriptSrc on the same entry — both
tags will be injected.
Template Filtering
enabled_on.templates and disabled_on.templates accept the same page
type strings the renderer uses:
index,product,collection,cart,page,blog,article,search,404,passwordcustomers/account,customers/login,customers/register
* matches every template (useful for “all pages except checkout”).
Settings Substitution
ForinlineHtml, {{settings.<id>}} is replaced with the merchant’s
value at render time. Common picker shapes are coerced to strings:
| Setting type | Substituted value |
|---|---|
text, textarea, select, radio, range, number | The raw string/number |
checkbox | "true" / "false" |
color | #RRGGBB hex |
image_picker, file_picker | The picker’s src or url |
scriptSrc, merchant settings are JSON-stringified onto a
data-config attribute on the script tag:
Inject Targets
target | Injection point | Use for |
|---|---|---|
head | Inside <head> after the loader stub | Standard async pixels and trackers |
body | Just before </body> | Floating widgets, modals, chat bubbles |
compliance_head | Top of <head>, synchronous | Consent banners, cookie blockers — runs before other scripts so it can block them |
Verifying Installation
config/settings_data.json (via the theme file API or CLI) and
check current.blocks["my-popup-app/welcome-popup"].
The settings_data.json entry should look like:
Customizer State
Embed state is namespaced by the theme it was enabled on:Canonical Example
The Channels Hub marketplace app is a full storefront-embed example. It uses an embed (rather than an app script) so the merchant can pick which chat provider runs per theme.See Also
- App Scripts — auto-load on every page, no merchant toggle.
- Theme Blocks — Liquid blocks merchants drop into theme sections, not floating overlays.
- Web Pixels — sandboxed customer-event listeners.