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 Hooks for App Bridge
The @launchmystore/app-bridge-react package provides React hooks and components for seamless App Bridge integration in React apps.
Installation
npm install @launchmystore/app-bridge-react
AppBridgeProvider
Wrap your app with AppBridgeProvider to enable App Bridge hooks:
import { AppBridgeProvider } from '@launchmystore/app-bridge-react';
function App() {
const apiKey = process.env.NEXT_PUBLIC_APP_CLIENT_ID;
const host = new URLSearchParams(location.search).get('host');
return (
<AppBridgeProvider apiKey={apiKey} host={host}>
<MyApp />
</AppBridgeProvider>
);
}
Props
| Prop | Type | Required | Description |
|---|
apiKey | string | Yes | Your app’s Client ID |
host | string | Yes | Encoded host from URL params |
children | ReactNode | Yes | Child components |
Core Hooks
useAppBridge
Access the App Bridge instance:
import { useAppBridge } from '@launchmystore/app-bridge-react';
function MyComponent() {
const app = useAppBridge();
const handleClick = () => {
app.dispatch({
type: 'TOAST',
payload: { message: 'Hello!' }
});
};
return <button onClick={handleClick}>Say Hello</button>;
}
useSessionToken
Get and manage session tokens:
import { useSessionToken } from '@launchmystore/app-bridge-react';
function MyComponent() {
const { token, loading, error, refresh } = useSessionToken();
const fetchData = async () => {
if (!token) return;
const response = await fetch('/api/data', {
headers: { 'Authorization': `Bearer ${token}` }
});
return response.json();
};
if (loading) return <div>Loading...</div>;
if (error) return <div>Auth error: {error.message}</div>;
return (
<div>
<button onClick={fetchData}>Fetch Data</button>
<button onClick={refresh}>Refresh Token</button>
</div>
);
}
Returns:
| Field | Type | Description |
|---|
token | string | null | Current JWT token |
loading | boolean | Token is being fetched |
error | Error | null | Token fetch error |
refresh | () => void | Force token refresh |
UI Hooks
useToast
Show toast notifications:
import { useToast } from '@launchmystore/app-bridge-react';
function MyComponent() {
const showToast = useToast();
const handleSave = async () => {
try {
await saveData();
showToast('Saved successfully!');
} catch (error) {
showToast('Failed to save', { isError: true });
}
};
return <button onClick={handleSave}>Save</button>;
}
Options:
showToast(message: string, options?: {
duration?: number; // Default 5000ms
isError?: boolean; // Error styling
});
useModal
Open confirmation modals:
import { useModal } from '@launchmystore/app-bridge-react';
function DeleteButton({ onDelete }) {
const { openModal, closeModal } = useModal();
const handleDelete = async () => {
const confirmed = await openModal({
title: 'Delete Item?',
message: 'This action cannot be undone.',
primaryAction: { content: 'Delete', destructive: true },
secondaryAction: { content: 'Cancel' }
});
if (confirmed) {
await onDelete();
}
};
return <button onClick={handleDelete}>Delete</button>;
}
Returns:
| Field | Type | Description |
|---|
openModal | (options) => Promise<boolean> | Open modal, returns true if primary clicked |
closeModal | () => void | Close modal programmatically |
useLoading
Control the loading bar:
import { useLoading } from '@launchmystore/app-bridge-react';
function MyComponent() {
const { startLoading, stopLoading, isLoading } = useLoading();
const handleFetch = async () => {
startLoading();
try {
await fetchData();
} finally {
stopLoading();
}
};
return (
<button onClick={handleFetch} disabled={isLoading}>
{isLoading ? 'Loading...' : 'Fetch Data'}
</button>
);
}
useFullscreen
Toggle fullscreen mode:
import { useFullscreen } from '@launchmystore/app-bridge-react';
function VideoPlayer() {
const { isFullscreen, enterFullscreen, exitFullscreen, toggleFullscreen } = useFullscreen();
return (
<div>
<video src="video.mp4" />
<button onClick={toggleFullscreen}>
{isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}
</button>
</div>
);
}
Navigation Hooks
useNavigate
Navigate within the admin:
import { useNavigate } from '@launchmystore/app-bridge-react';
function ProductLink({ productId }) {
const navigate = useNavigate();
const goToProduct = () => {
navigate(`/admin/products/${productId}`);
};
const openExternal = () => {
navigate('https://external.com', { external: true, newTab: true });
};
return (
<>
<button onClick={goToProduct}>View Product</button>
<button onClick={openExternal}>External Link</button>
</>
);
}
Options:
navigate(url: string, options?: {
newContext?: boolean; // Leave app context
external?: boolean; // External URL
newTab?: boolean; // Open in new tab
});
useTitleBar
Update the title bar:
import { useTitleBar } from '@launchmystore/app-bridge-react';
function ProductPage({ product }) {
const { setTitle, setPrimaryAction, setSecondaryActions, setBreadcrumbs } = useTitleBar();
useEffect(() => {
setTitle(product.title);
setPrimaryAction({
content: 'Save',
onAction: handleSave
});
setSecondaryActions([
{ content: 'Preview', onAction: handlePreview },
{ content: 'Delete', onAction: handleDelete, destructive: true }
]);
setBreadcrumbs([
{ content: 'Products', url: '/products' }
]);
return () => {
// Cleanup on unmount
setTitle('');
setPrimaryAction(null);
};
}, [product]);
return <div>...</div>;
}
Set up app navigation:
import { useNavigationMenu } from '@launchmystore/app-bridge-react';
import { useLocation } from 'react-router-dom';
function AppNavigation() {
const location = useLocation();
const setNavigation = useNavigationMenu();
useEffect(() => {
setNavigation({
items: [
{ label: 'Dashboard', destination: '/' },
{ label: 'Reviews', destination: '/reviews' },
{ label: 'Analytics', destination: '/analytics' },
{ label: 'Settings', destination: '/settings' }
],
active: location.pathname
});
}, [location.pathname]);
return null; // This hook handles navigation in the host
}
Resource Picker Hooks
useResourcePicker
Open resource selection modals:
import { useResourcePicker } from '@launchmystore/app-bridge-react';
function ProductSelector({ onSelect }) {
const pickProducts = useResourcePicker('product');
const handlePick = async () => {
const selection = await pickProducts({
multiple: true,
filter: { status: 'active' }
});
if (selection) {
onSelect(selection);
}
};
return <button onClick={handlePick}>Select Products</button>;
}
Resource-specific hooks:
import {
useProductPicker,
useCollectionPicker,
useCustomerPicker,
useOrderPicker,
useFilePicker
} from '@launchmystore/app-bridge-react';
function MyComponent() {
const pickProducts = useProductPicker();
const pickCollections = useCollectionPicker();
const pickCustomers = useCustomerPicker();
const pickOrders = useOrderPicker();
const pickFiles = useFilePicker();
// Each returns (options?) => Promise<selection | null>
}
Save Bar Hook
useContextualSaveBar
Show save/discard bar for unsaved changes:
import { useContextualSaveBar } from '@launchmystore/app-bridge-react';
import { useState, useCallback } from 'react';
function EditForm({ initialData }) {
const [data, setData] = useState(initialData);
const [isDirty, setIsDirty] = useState(false);
const { showSaveBar, hideSaveBar } = useContextualSaveBar();
const handleChange = (field, value) => {
setData({ ...data, [field]: value });
setIsDirty(true);
showSaveBar({
onSave: handleSave,
onDiscard: handleDiscard
});
};
const handleSave = useCallback(async () => {
await saveData(data);
setIsDirty(false);
hideSaveBar();
}, [data]);
const handleDiscard = useCallback(() => {
setData(initialData);
setIsDirty(false);
hideSaveBar();
}, [initialData]);
return (
<form>
<input
value={data.title}
onChange={(e) => handleChange('title', e.target.value)}
/>
</form>
);
}
Leave Confirmation Hook
useLeaveConfirmation
Warn users about unsaved changes:
import { useLeaveConfirmation } from '@launchmystore/app-bridge-react';
function EditPage() {
const [isDirty, setIsDirty] = useState(false);
useLeaveConfirmation(
isDirty,
'You have unsaved changes. Are you sure you want to leave?'
);
return <form>...</form>;
}
Utility Hooks
useClipboard
Copy/paste with clipboard:
import { useClipboard } from '@launchmystore/app-bridge-react';
function ShareLink({ url }) {
const { copy, paste, copied } = useClipboard();
return (
<div>
<input value={url} readOnly />
<button onClick={() => copy(url)}>
{copied ? 'Copied!' : 'Copy'}
</button>
</div>
);
}
usePrint
Trigger browser print:
import { usePrint } from '@launchmystore/app-bridge-react';
function Invoice() {
const print = usePrint();
return (
<div>
<div className="invoice-content">...</div>
<button onClick={print}>Print Invoice</button>
</div>
);
}
useShare
Open native share dialog (mobile):
import { useShare } from '@launchmystore/app-bridge-react';
function ShareButton({ title, url }) {
const share = useShare();
return (
<button onClick={() => share({ title, url })}>
Share
</button>
);
}
useScanner
Open barcode/QR scanner (mobile):
import { useScanner } from '@launchmystore/app-bridge-react';
function InventoryScanner({ onScan }) {
const { scan, scanning } = useScanner();
const handleScan = async () => {
const result = await scan();
if (result) {
onScan(result.data);
}
};
return (
<button onClick={handleScan} disabled={scanning}>
{scanning ? 'Scanning...' : 'Scan Barcode'}
</button>
);
}
Event Subscription
useAppBridgeSubscription
Subscribe to App Bridge events:
import { useAppBridgeSubscription } from '@launchmystore/app-bridge-react';
function MyComponent() {
useAppBridgeSubscription('LIFECYCLE_FOCUS', () => {
// Refresh data when app gains focus
refetchData();
});
useAppBridgeSubscription('LIFECYCLE_VISIBILITY', (data) => {
if (data.visible) {
// App became visible
}
});
return <div>...</div>;
}
Complete Example
import {
AppBridgeProvider,
useAppBridge,
useSessionToken,
useToast,
useModal,
useLoading,
useNavigate,
useTitleBar,
useResourcePicker,
useContextualSaveBar
} from '@launchmystore/app-bridge-react';
function App() {
return (
<AppBridgeProvider
apiKey={process.env.NEXT_PUBLIC_APP_CLIENT_ID}
host={new URLSearchParams(location.search).get('host')}
>
<ProductReviews />
</AppBridgeProvider>
);
}
function ProductReviews() {
const [reviews, setReviews] = useState([]);
const [selectedProduct, setSelectedProduct] = useState(null);
const [isDirty, setIsDirty] = useState(false);
const { token } = useSessionToken();
const showToast = useToast();
const { openModal } = useModal();
const { startLoading, stopLoading } = useLoading();
const navigate = useNavigate();
const pickProduct = useResourcePicker('product');
const { showSaveBar, hideSaveBar } = useContextualSaveBar();
const { setTitle, setPrimaryAction } = useTitleBar();
// Set up title bar
useEffect(() => {
setTitle('Product Reviews');
setPrimaryAction({
content: 'Add Review',
onAction: handleAddReview
});
}, []);
// Handle unsaved changes
useEffect(() => {
if (isDirty) {
showSaveBar({
onSave: handleSave,
onDiscard: handleDiscard
});
} else {
hideSaveBar();
}
}, [isDirty]);
const handleAddReview = async () => {
const selection = await pickProduct();
if (selection && selection.length > 0) {
setSelectedProduct(selection[0]);
setIsDirty(true);
}
};
const handleSave = async () => {
startLoading();
try {
await fetch('/api/reviews', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ productId: selectedProduct.id })
});
showToast('Review saved!');
setIsDirty(false);
} catch (error) {
showToast('Failed to save', { isError: true });
} finally {
stopLoading();
}
};
const handleDiscard = () => {
setSelectedProduct(null);
setIsDirty(false);
};
const handleDelete = async (reviewId) => {
const confirmed = await openModal({
title: 'Delete Review?',
message: 'This action cannot be undone.',
primaryAction: { content: 'Delete', destructive: true },
secondaryAction: { content: 'Cancel' }
});
if (confirmed) {
await deleteReview(reviewId);
showToast('Review deleted');
}
};
return (
<div className="reviews-page">
{reviews.map(review => (
<ReviewCard
key={review.id}
review={review}
onDelete={() => handleDelete(review.id)}
/>
))}
</div>
);
}
TypeScript Support
All hooks are fully typed:
import { useResourcePicker } from '@launchmystore/app-bridge-react';
import type { Product } from '@launchmystore/app-bridge-react';
function ProductPicker() {
const pickProducts = useResourcePicker<Product>('product');
const handlePick = async () => {
const selection: Product[] | null = await pickProducts({
multiple: true
});
if (selection) {
selection.forEach(product => {
console.log(product.title, product.id);
});
}
};
}