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.
App Scripts
App scripts are JavaScript files your app declares inapp.json that the
storefront loads on every page — no theme edits, no merchant action beyond
installing the app. The host inserts a non-blocking loader in
content_for_header that fetches /api/apps/extensions and injects every
declared script via requestIdleCallback.
Use app scripts when your app needs to run on the storefront and the
effect is delivered entirely by a <script> (chat widgets, pixels,
typeahead overlays, floating buttons, badge swaps, etc.).
For Liquid placement merchants drag into theme sections, use
Storefront Blocks. For sandboxed
customer-event listeners, use Web Pixels.
When to Use App Scripts
| Need | Use |
|---|---|
| Floating chat / WhatsApp / Calendly button on every page | App script |
| GA4 / Meta Pixel / TikTok / Hotjar / Clarity | App script |
| Storefront-wide search typeahead, points pill, pre-order badge | App script |
| Liquid block the merchant places inside a section | Storefront Block |
Sandboxed listener for page_viewed, cart_updated, etc. | Web Pixel |
<head> / </body> overlay the merchant can toggle per-theme | Storefront Embed |
How It Works
- Your app’s
app.jsondeclares one or moreappScriptsentries with asrcURL. - After install, the storefront fetches
/api/apps/extensions?domainSlug=…on every page. The response includes ascripts[]array merged from the backend (DB-backed installs) and local manifests on disk. - A small loader stub in
content_for_headerschedulesrequestIdleCallback(falls back tosetTimeout(0)), fetches the extensions list, and appends one<script>per entry — de-duplicated bysrcso SPA navigations don’t double-inject. - Your script runs on the storefront, scoped to that merchant’s domain.
src/pages/api/themes/render-page.js
and surfaces local manifests via
_localManifests.js::getLocalAppScripts().
You don’t need to invoke either — declaring appScripts in app.json is
enough.
Manifest
Declare app scripts underextensions.appScripts in your app.json:
Fields
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique identifier within your app. Surfaced in the loader response and on the injected <script data-app-id="…"> tag for easy debugging. |
src | string | — | URL the loader appends as <script src="…">. May be a relative path on the host (e.g. /api/apps/<handle>/loader) or an absolute external URL. Required. |
loadStrategy | "defer" | "async" | "defer" | Maps directly to the <script> attribute. Use defer when your code touches the DOM, async for fire-and-forget pixels. |
position | "head" | "body" | "body" | Where the loader appends the script tag. head runs marginally earlier; body is fine for most widgets. |
appId | string | <appHandle> | Optional override surfaced on the injected <script data-app-id> attribute. |
config | object | {} | Reserved for future per-script merchant config. Not consumed by the current loader. |
Multiple scripts per app
Declare more than one entry if your app needs to inject a separate file at a different position or load strategy:src, so reloading or SPA-navigating won’t double-load.
The loader.js Endpoint
Thesrc URL almost always points at an endpoint inside your app — so the
script body can read the merchant’s saved settings and bake them in. The
endpoint must respond with Content-Type: application/javascript.
Recommended response headers
Disabled / unconfigured state
Return a no-op comment when the merchant hasn’t enabled the app yet — the loader still appends a<script> tag, but it does nothing.
Resolving the merchant slug
The loader passesdomainSlug as a query string, but you should also
accept the x-domain-slug header and the request Host:
Minimal Example
A complete script-type app has five files:app.json
loader.js
The loader returns an IIFE that runs on the storefront. Bake the merchant
slug and any safe config values into the response so the script doesn’t
need to make a second round trip just to know which store it’s on.
config.js
File-backed merchant config under data/<handle>/<safeSlug>.config.json.
See Channels Hub
for the canonical template — readConfig(slug) / writeConfig(slug, cfg)
with the slug normalised through path.basename and a 60s in-memory cache.
Verifying The Loader Is Wired
Two HTTP calls confirm everything is connected end-to-end:scripts[] doesn’t contain your entry, the local manifest cache may be
stale — touch the app.json or wait 60s.
CSP & Cross-Origin Notes
The loader injects<script src> tags directly into the storefront page,
so the merchant’s storefront origin must allow the script’s origin. For
scripts served by your own host (relative paths under /api/apps/…) this
is automatic. For absolute external URLs, the merchant’s CSP — if any —
must list the script host in script-src.
If you’re injecting a third-party vendor snippet (e.g. Tawk, Clarity,
Hotjar), it’s safer to have your loader return a stub that
appends the vendor script itself rather than redirecting to the vendor
URL — that way you control caching and can disable the integration without
the merchant changing their CSP.
Canonical Examples
Several first-party apps in the CustomerLMS catalog use this pattern and make a good reference:- Channels Hub — 4-in-1 chat aggregator (Tawk + Intercom + Crisp + WhatsApp). Loader emits each vendor snippet only when its credential is set.
- Smart Pixel Manager — Loads GA4 + Meta + TikTok with consent gating wired through a storefront cookie.
- Early Access — Per-product pre-order labels.
- Restock Alerts — In-page “Notify me” form on sold-out variants.
- Cobalt Search — Search typeahead overlay attached to the theme’s search input.
See Also
- Extensions Overview — full manifest reference.
- Storefront Blocks — Liquid blocks merchants place inside theme sections (different surface, different lifecycle).
- Web Pixels — sandboxed customer-event scripts
for
page_viewed,cart_updated,checkout_completed. - App Bridge Overview — the postMessage contract used by admin/checkout iframes (app scripts run in-page and don’t need App Bridge).