Documentation Index
Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
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.json under extensions.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.json under
current.blocks["<appHandle>/<embedHandle>"].
- On every storefront render,
appEmbedLoader.js
injects enabled embeds into the rendered HTML according to each entry’s
target.
Default: an embed with no entry in settings_data.json is disabled.
This matches Shopify’s behaviour — merchants must opt in.
Manifest
{
"handle": "my-popup-app",
"name": "Welcome Popup",
"version": "1.0.0",
"extensions": {
"storefrontEmbeds": [
{
"handle": "welcome-popup",
"title": "Welcome popup",
"target": "body",
"inlineHtml": "<div class='welcome-modal'>Welcome — use code {{settings.code}}</div>",
"schema": {
"name": "Welcome popup",
"settings": [
{ "type": "text", "id": "code", "label": "Coupon code", "default": "WELCOME10" },
{ "type": "color", "id": "accent", "label": "Accent", "default": "#3B82F6" }
]
},
"enabled_on": { "templates": ["index", "product"] }
}
]
}
}
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. |
You can declare both 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, password
customers/account, customers/login, customers/register
Wildcard * matches every template (useful for “all pages except checkout”).
"enabled_on": { "templates": ["product", "collection"] }
Settings Substitution
For inlineHtml, {{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 |
For scriptSrc, merchant settings are JSON-stringified onto a
data-config attribute on the script tag:
<script async src="https://my-app.example.com/embed.js"
data-lms-app-embed="my-popup-app/welcome-popup"
data-app-id="my-popup-app"
data-config="{"code":"WELCOME10"}"></script>
Read it back inside your embed script via:
const el = document.currentScript;
const cfg = JSON.parse(el.dataset.config || '{}');
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
# Manifest is surfaced
curl 'http://raja337276.localhost:3000/api/apps/extensions?domainSlug=raja337276' \
| jq '.embeds[] | select(.embedId == "my-popup-app/welcome-popup")'
# Merchant has enabled it on the active theme
cat public/themes/<themeId>/config/settings_data.json \
| jq '.current.blocks["my-popup-app/welcome-popup"]'
The settings_data.json entry should look like:
{
"type": "@app",
"disabled": false,
"settings": { "code": "WELCOME10", "accent": "#3B82F6" }
}
Customizer State
Embed state is namespaced by the theme it was enabled on:
public/themes/<themeId>/config/settings_data.json
↳ current.blocks
↳ "<appHandle>/<embedHandle>"
↳ { type: "@app", disabled: false, settings: { ... } }
When a merchant duplicates a theme, the embed state copies with it — the
merchant can enable an embed on a staging theme without affecting
production.
Canonical Example
See public/marketplace-apps/apps/channels-hub/ for a full storefront-embed
manifest. Channels Hub 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.