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.
React Components
@launchmystore/app-bridge-react ships two kinds of components:
- Chrome wrappers —
<TitleBar />, <NavigationMenu />,
<Loading />. These render nothing visible. They render to the host
chrome by calling the matching App Bridge action under the hood.
- In-iframe UI primitives —
<AdminBlock>, <AdminAction>,
<AdminPrintAction>, layout/text/form primitives. These render
inside your extension iframe and give you a consistent look without
pulling in a separate design system.
All components require an <AppBridgeProvider> ancestor — see
React Hooks.
Chrome wrappers
These are declarative shells around their matching hooks. They return
null and re-render the host chrome whenever their props change. Use
them when you’d rather think in JSX than in imperative .update()
calls.
<TitleBar />
Sets the admin page title, primary/secondary action buttons, and
breadcrumbs. Wraps useTitleBar.
import { TitleBar } from '@launchmystore/app-bridge-react';
export default function ProductPage({ product }) {
const [saving, setSaving] = useState(false);
return (
<>
<TitleBar
title={product.title}
breadcrumbs={[{ label: 'Products', url: '/products' }]}
primaryAction={{
label: 'Save',
loading: saving,
onAction: async () => {
setSaving(true);
await saveProduct(product);
setSaving(false);
},
}}
secondaryActions={[
{ label: 'Duplicate', onAction: duplicate },
{ label: 'Delete', destructive: true, onAction: remove },
]}
/>
<ProductForm product={product} />
</>
);
}
Props (TitleBarProps)
| Prop | Type | Description |
|---|
title | string | Required. Shown as the page title in the host admin chrome. |
primaryAction | { label, onAction?, disabled?, loading?, destructive? } | Right-aligned filled button. |
secondaryActions | Array<{ label, onAction?, disabled?, destructive? }> | Plain buttons left of the primary. |
breadcrumbs | Array<{ label, url? | destination? }> | Rendered as a chevron-separated trail before the title. |
Re-renders trigger an update() on the underlying action — flip
loading: true on the primary action to show a spinner without
re-creating the bar.
Registers a left-rail navigation menu for the app’s admin pages. Wraps
useNavigationMenu.
import { NavigationMenu } from '@launchmystore/app-bridge-react';
import { useRouter } from 'next/router';
export default function AppLayout({ children }) {
const router = useRouter();
return (
<>
<NavigationMenu
items={[
{ label: 'Dashboard', url: '/' },
{ label: 'Campaigns', url: '/campaigns', badge: 'New' },
{ label: 'Settings', url: '/settings' },
]}
active={router.pathname}
onNavigate={(url) => router.push(url)}
/>
{children}
</>
);
}
Props (NavigationMenuProps)
| Prop | Type | Description |
|---|
items | Array<{ label, url, disabled?, icon?, badge? }> | Required. Order = render order. |
active | string | URL of the currently-active item — host highlights it. |
onNavigate | (url: string) => void | Called when the user clicks an item. Use this to push to your router. |
The host swallows the click and fires this callback — you’re
responsible for the in-iframe route change (e.g. router.push(url)).
<Loading />
Toggles the global loading bar in the host chrome (the thin progress
strip at the top of the admin). Wraps
useLoading.
import { Loading } from '@launchmystore/app-bridge-react';
function ProductDetail({ id }) {
const { data, isLoading } = useAppQuery(`/api/v1/products/${id}`);
return (
<>
<Loading loading={isLoading} />
{data ? <ProductView product={data} /> : null}
</>
);
}
Props (LoadingProps)
| Prop | Type | Default | Description |
|---|
loading | boolean | true | When true, the host loading bar is shown. The bar hides on unmount automatically. |
Contextual save bar
There’s no <ContextualSaveBar /> component — the save bar’s
imperative API (setSaveLoading, setDiscardLoading) is a poor fit
for declarative JSX. Use the
useContextualSaveBar
or useDirtyState
hooks instead:
import { useDirtyState, TextField } from '@launchmystore/app-bridge-react';
function SettingsForm({ initial }) {
const [form, setForm] = useState(initial);
const { setDirty } = useDirtyState({
onSave: () => saveSettings(form),
onDiscard: () => setForm(initial),
});
return (
<TextField
label="Store name"
value={form.name}
onChange={(name) => {
setForm((s) => ({ ...s, name }));
setDirty(true);
}}
/>
);
}
Admin extension containers
Use these as the outermost element of any extension iframe. They give
you a card / modal / print layout that matches the host admin look
without you having to import a design system.
<AdminBlock>
Outer container for block extension targets (product.details.block,
order.details.block, etc.). Renders a white card with a title and
padding.
import { AdminBlock, BlockStack, Text, Badge } from '@launchmystore/app-bridge-react';
export default function Reviews() {
return (
<AdminBlock title="Reviews">
<BlockStack gap="200">
<Text variant="headingLg">4.8 / 5</Text>
<Text tone="subdued">124 customer reviews</Text>
<Badge tone="success">Verified app</Badge>
</BlockStack>
</AdminBlock>
);
}
Props (AdminBlockProps) — title?: string, padding?: number (default 16), plus any HTMLDivElement attributes.
<AdminAction>
Container for modal-style action extension targets
(admin.order-details.action.render, etc.). Renders a scrolling body
with a sticky footer containing the primary + secondary action
buttons. The footer handles button states (loading / destructive /
disabled) for you.
import { AdminAction, BlockStack, TextField } from '@launchmystore/app-bridge-react';
export default function RefundDialog({ order, onClose }) {
const [amount, setAmount] = useState('');
const [submitting, setSubmitting] = useState(false);
return (
<AdminAction
primaryAction={{
content: 'Issue refund',
destructive: true,
loading: submitting,
onAction: async () => {
setSubmitting(true);
await issueRefund(order.id, amount);
onClose();
},
}}
secondaryActions={[
{ content: 'Cancel', onAction: onClose },
]}
>
<BlockStack gap="300">
<TextField
label="Refund amount"
value={amount}
onChange={setAmount}
/>
</BlockStack>
</AdminAction>
);
}
Props (AdminActionProps)
primaryAction?: { content, onAction?, loading?, destructive?, disabled? }
secondaryActions?: Array<{ content, onAction?, disabled? }>
- Plus any
HTMLDivElement attributes.
<AdminPrintAction>
Container for print extension targets
(admin.order-details.print.render). Renders a full-page print
layout with @media print rules that reset margins and colors. Use
usePrintReady() from inside the tree to signal the host that the
content has finished loading and can be sent to the printer.
import { AdminPrintAction, usePrintReady, Heading, Text } from '@launchmystore/app-bridge-react';
export default function PackingSlip({ order }) {
const { setReady } = usePrintReady();
const { data } = useAppQuery(`/api/v1/orders/${order.id}`);
useEffect(() => { if (data) setReady(true); }, [data]);
if (!data) return null;
return (
<AdminPrintAction>
<Heading level={1}>Packing slip</Heading>
<Text>Order #{data.order_number}</Text>
</AdminPrintAction>
);
}
The host inspects data-print-ready="true" on the container and only
then calls the browser print dialog.
Layout primitives
Token-driven flex layouts. Gaps use the spacing scale "0" | "100" | "200" | "300" | "400" | "500" (= 0 | 4 | 8 | 12 | 16 | 24 px).
| Component | Purpose |
|---|
<BlockStack> | Vertical flex column. gap defaults to "200". |
<InlineStack> | Horizontal flex row. gap, align, blockAlign. |
<Box> | Generic <div> with token padding / background. |
<Divider> | Thin horizontal rule using tokens.color.border. |
<BlockStack gap="300">
<Text variant="headingMd">Order #1234</Text>
<InlineStack gap="200" align="space-between">
<Text>Total</Text>
<Text fontWeight="semibold">$99.00</Text>
</InlineStack>
<Divider />
</BlockStack>
Typography
| Component | Purpose |
|---|
<Text> | Body / heading text. variant (bodyXs–bodyLg, headingSm–headingXl), tone (base, subdued, critical, success, caution), fontWeight, as. |
<Heading> | h1–h6 with level: 1-6. Wraps <Text> with sensible defaults. |
<Link> | Anchor with token color + hover underline. Pass external for target="_blank" rel="noreferrer". |
<Heading level={2}>Inventory</Heading>
<Text tone="subdued">Last synced 3 minutes ago.</Text>
<Link external href="https://launchmystore.io/docs">Read the docs</Link>
Display
| Component | Purpose |
|---|
<Banner> | Tone-coloured callout. tone: 'info' | 'warning' | 'critical' | 'success', title, onDismiss. |
<Badge> | Pill label. tone (info, success, warning, critical, attention, new), size: 'small' | 'medium'. |
<Image> | <img> with rounded corners + lazy-loading default. |
<Icon> | Inline SVG icon. Pass a source SVG path or use the small built-in set. |
<EmptyState> | Centered “nothing here yet” block with optional <Button> action. |
<ProgressIndicator> | Determinate progress bar. value: 0-100. |
<Spinner> | Indeterminate circular spinner. size: 'small' | 'large'. |
<Banner tone="critical" title="Sync failed" onDismiss={dismiss}>
Couldn't reach the upstream provider. We'll retry in 5 minutes.
</Banner>
Controlled inputs. All call onChange(value) with the next value
(no event object). All accept label, helpText, error.
| Component | Purpose |
|---|
<TextField> | <input> / <textarea> (multiline). Supports prefix / suffix slots, type: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'number'. |
<NumberField> | TextField with inputMode="numeric" + numeric onChange. min, max, step. |
<MoneyField> | TextField with currency prefix. currency: 'USD' (etc.). |
<DateField> | <input type="date"> returning ISO YYYY-MM-DD. |
<Select> | Native <select>. options: Array<{ label, value }>. |
<Checkbox> | Single boolean. |
<ChoiceList> | Radio (single) or checkbox (allowMultiple) group. choices: Array<{ label, value, helpText? }>. |
<Button> | variant: 'primary' | 'secondary' | 'tertiary' | 'plain', tone: 'base' | 'critical' | 'success', size, loading. |
<Form> | <form> that calls onSubmit(e) and prevents default. Useful for Enter to submit. |
import { Form, TextField, MoneyField, Select, Button, BlockStack } from '@launchmystore/app-bridge-react';
function CreateDiscount() {
const [code, setCode] = useState('');
const [amount, setAmount] = useState('10');
const [kind, setKind] = useState('percentage');
return (
<Form onSubmit={() => createDiscount({ code, amount, kind })}>
<BlockStack gap="300">
<TextField
label="Discount code"
value={code}
onChange={setCode}
helpText="Customers enter this at checkout"
/>
<Select
label="Kind"
value={kind}
onChange={setKind}
options={[
{ label: 'Percentage off', value: 'percentage' },
{ label: 'Fixed amount off', value: 'fixed' },
]}
/>
{kind === 'percentage'
? <NumberField label="Percent off" value={amount} onChange={setAmount} min={0} max={100} />
: <MoneyField label="Amount off" value={amount} onChange={setAmount} currency="USD" />}
<Button variant="primary">Create</Button>
</BlockStack>
</Form>
);
}
Design tokens
All components read from a shared token set in
packages/app-bridge-react/src/components/tokens.ts:
tokens.color.* — base, surface, border, primary, critical, success,
info, warning, plus *Subdued / *Border variants for banners.
tokens.font.* — family, size.{xs,sm,md,lg,xl}, weight.*,
lineHeight.*.
tokens.radius.* — sm (4), md (6), lg (8).
tokens.shadow.* — sm for cards.
You can override per-component via the standard style prop — the
token style is spread first, your style last.
When to use components vs. hooks
| Goal | Use |
|---|
| Set the title bar declaratively | <TitleBar /> |
Toggle the title bar’s primaryAction.loading | Either — the component re-renders. |
| Open a confirmation modal once on click | useConfirmationModal hook |
| Render a scrolling-with-sticky-footer action page | <AdminAction> |
| Show / hide the host loading bar | <Loading loading={...} /> |
| Manage a contextual save bar tied to form state | useDirtyState hook |
| Open a resource picker imperatively | useResourcePicker / useApi().picker |
Both styles use the same wire protocol — pick whichever reads better
in the component you’re writing.
See Also
- React Hooks — every hook the components
wrap, plus the data-fetching / cart / lifecycle hooks that have no
component equivalent.
- Actions Reference — raw payload shapes for
every App Bridge action.
- App Bridge Overview — the iframe ↔ host
postMessage contract underneath all of this.