App Bridge SDK: build admin and storefront extensions
App Bridge is the JavaScript SDK that connects your app's iframe to the LaunchMyStore admin host. With one createApp call and a handful of dispatch actions, you can show toasts, open modals, pick resources, set the title bar, request session tokens, and drive 21 other host capabilities — all without ever exposing the merchant's storefront origin to your iframe.
What is App Bridge
Admin extensions run inside a sandboxed iframe in the merchant's admin. The host page (the admin shell) and your iframe sit on different origins, so you can't reach into each other directly. App Bridge wraps window.postMessage into a clean request/response API:
- Your iframe calls
app.dispatch('TOAST_SHOW', { message: 'Saved' }). - The SDK posts a structured message to the host with a unique id.
- The host runs the action (renders the toast, opens the modal, queries the database, whatever).
- If the action returns data, the host posts a response back; the SDK resolves your promise.
Install and bootstrap
npm install @launchmystore/app-bridge @launchmystore/app-bridge-react
The host injects two values into your iframe URL: ?apiKey=...&host=... (where host is a base64-encoded origin). Pass them to createApp:
import { createApp } from '@launchmystore/app-bridge';
const params = new URLSearchParams(window.location.search);
const app = createApp({
apiKey: params.get('apiKey'),
host: params.get('host'),
});
That's it — you now have a configured App instance and the SDK is listening for responses from the host.
The 21 action families
App Bridge groups actions into family modules. The full set:
- Toast —
TOAST_SHOW,TOAST_HIDE; success/error/info variants. - Modal — open a host-rendered modal with an iframe URL or inline title + body.
- ResourcePicker — 11 picker types:
collection,product,blog,article,page,menu,customer,order,product_variant,file,metaobject. - TitleBar — set page title + primary/secondary action buttons.
- NavigationMenu — register secondary nav items under your app.
- ContextualSaveBar — the "Unsaved changes" bar at the top of the admin.
- Loading — toggle the top progress bar.
- Fullscreen — expand your iframe to fill the viewport.
- LeaveConfirmation — warn before navigating away with unsaved state.
- SessionToken —
app.getSessionToken()returns a short-lived JWT for API calls. - User — current staff identity (id, name, email, locale).
- Config — store config (currency, country, plan).
- Environment — runtime info (host version, theme mode).
- Features — feature flags exposed by the host.
- Print — trigger
window.printon a host page. - Share — native share sheet via the host.
- Clipboard —
write/copyiframe-side;read/pastevia the host (Chrome blocks clipboard read in cross-origin iframes). - Scanner — barcode/QR scan via host camera.
- History — push/replace state in the admin URL bar.
- Lifecycle —
READY,BEFORE_UNLOADnotifications. - Redirect —
app.redirect.dispatch({ url, newContext })— navigate the parent.
Dispatching actions
Fire-and-forget:
app.dispatch('TOAST_SHOW', {
message: 'Settings saved',
duration: 3000,
isError: false,
});
Request/response (waits for the host to reply):
const result = await app.dispatchAndWait('RESOURCE_PICKER_OPEN', {
type: 'product',
multiple: true,
});
console.log(result.selection); // array of selected products
Session tokens for API calls
Your app's backend needs to know which merchant is calling it. App Bridge issues short-lived JWTs signed with your client secret:
const token = await app.getSessionToken();
fetch('/api/save-settings', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ ... }),
});
Tokens are cached and auto-refreshed by the SDK. Verify them server-side using your app's client secret.
React bindings
If you're building with React, install @launchmystore/app-bridge-react for hooks and providers:
import { AppBridgeProvider, useAppBridge } from '@launchmystore/app-bridge-react';
function App() {
return (
<AppBridgeProvider apiKey={apiKey} host={host}>
<SettingsPage />
</AppBridgeProvider>
);
}
function SettingsPage() {
const app = useAppBridge();
return <button onClick={() => app.dispatch('TOAST_SHOW', { message: 'Hi' })}>Toast</button>;
}
The postMessage wire format
If you're debugging or building a non-JS client, here's the contract the host listens for:
// iframe → host
{ type: 'APP_BRIDGE_ACTION', action: 'TOAST_SHOW', id: 'ab_123', payload: { ... } }
// host → iframe (response)
{ type: 'APP_BRIDGE_RESPONSE', action: 'TOAST_SHOW', id: 'ab_123', payload: { ... } }
// iframe self-resize
{ type: 'APP_BRIDGE_RESIZE', extensionId: 'your-manifest-id', height: 480 }
The host silently drops messages that don't carry type: 'APP_BRIDGE_ACTION'. For resize, the extensionId must match the manifest id the host passes via ?extensionId= — otherwise your iframe stays at the 200px default and looks empty.
FAQ
Do I have to use App Bridge for admin extensions?
Yes, in practice. The host enforces cross-origin isolation, so direct DOM access is impossible. App Bridge is the only sanctioned way to interact with the admin chrome (toasts, modals, navigation).
Where do admin extensions show up in the admin?
There are 26 verified injection points including product details, order details, customer details, collection details, discount details, gift card list, blog list, contact list, analytics, inventory, shipping settings, payment settings, POS settings, account settings, order create, and more. Each extension's manifest declares its target.
Does App Bridge work in checkout and post-purchase extensions?
Yes. Pass host: 'checkout' or host: 'post-purchase' as a sentinel value — the SDK detects these and treats the iframe as same-origin instead of decoding base64.
How do I open a modal from inside my app?
app.dispatch('MODAL_OPEN', { url: '/extensions/edit', title: 'Edit', size: 'large' }). The host renders the modal chrome and loads your URL in a nested iframe. Use MODAL_CLOSE to dismiss programmatically.
Why is my iframe stuck at 200px tall?
Almost always a mismatched extensionId on the resize message. Read the extensionId from your iframe's URL query and echo it back exactly in the APP_BRIDGE_RESIZE payload.
How long does a session token last?
The JWT carries a short expiry (typically a minute). The SDK caches it and refreshes automatically before it expires, so application code can call getSessionToken() as often as it likes without worrying about lifecycle.