Skip to main content

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

NeedUse
Per-theme on/off toggle in the customizerStorefront embed
Settings (colour, position) merchant can change per themeStorefront embed
Inline HTML overlay (badge, ribbon, banner)Storefront embed
Pixel or chat widget that should always be on once installedApp Script
Sandboxed customer-event listenerWeb Pixel
Liquid block dropped into a theme sectionStorefront Block

How Embeds Work

  1. Declare embeds in app.json under extensions.storefrontEmbeds.
  2. After install, embeds appear in the theme customizer under App embeds.
  3. 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>"].
  4. 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

FieldTypeDefaultDescription
handlestringUnique within your app. Combined with the app handle to form the embed key (<appHandle>/<embedHandle>). Required.
titlestringhandleLabel 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.
inlineHtmlstringRaw HTML wrapped in a <div data-lms-app-embed="…">…</div>. Supports {{settings.<id>}} placeholders, escaped at render time.
scriptSrcstringURL appended as <script async src="…" data-config="…">. Merchant settings are serialised onto data-config as JSON.
schemaobjectLiquid-style schema ({ name, settings }) used by the customizer. Same input types as theme block schemas.
enabled_onobject{ templates: ["index", "product"] } — embed runs only on listed page types. Use "*" for all.
disabled_onobject{ templates: ["404"] } — embed runs everywhere except listed. Used when enabled_on is absent.
appIdstring<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 typeSubstituted value
text, textarea, select, radio, range, numberThe raw string/number
checkbox"true" / "false"
color#RRGGBB hex
image_picker, file_pickerThe 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="{&quot;code&quot;:&quot;WELCOME10&quot;}"></script>
Read it back inside your embed script via:
const el = document.currentScript;
const cfg = JSON.parse(el.dataset.config || '{}');

Inject Targets

targetInjection pointUse for
headInside <head> after the loader stubStandard async pixels and trackers
bodyJust before </body>Floating widgets, modals, chat bubbles
compliance_headTop of <head>, synchronousConsent 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.