Quick start
Enable OTA in init(), let the SDK check for updates automatically, and drive the update UI with the useOtaUpdate hook — download progress, mandatory updates, and restart-to-apply.
OTA is part of the SDK you already installed — there is no extra package. You turn
it on in init(), and from then on the SDK can pull new JS bundles for your app.
1. Enable OTA in init()#
import ScaleBun from '@scalebun/react-native';
ScaleBun.init({ clientKey: 'skb_live_ck_…', apiUrl: 'https://api.scalebun.com/api/v1', ota: { enabled: true, // OTA is off until you opt in checkOnForeground: true, // check for a new bundle each time the app foregrounds channelOverride: 'production', // which channel this build follows (optional) mandatoryBlocksUi: true, // a required update blocks the UI until applied },});| Option | Default | What it does |
|---|---|---|
enabled | false | Master switch — OTA does nothing until this is true |
checkOnForeground | true | Automatically check for an update whenever the app returns to the foreground |
channelOverride | — | Follow a specific channel (e.g. production, beta). Falls back to the app's default channel |
mandatoryBlocksUi | false | When a release is marked mandatory, block the UI until it's applied |
With checkOnForeground: true, that's already a working setup: publish a bundle and
it reaches this app on its next foreground + restart. The rest of this page is about
controlling and surfacing updates in your UI.
2. Drive updates with useOtaUpdate#
useOtaUpdate is a React hook that exposes the update state and a sync() action —
ideal for a "check for updates" button, a download bar, or a mandatory-update gate.
import { useOtaUpdate } from '@scalebun/react-native';import DeviceInfo from 'react-native-device-info';
export function UpdateBanner() { const { sync, // () => Promise<SyncResult> restart, // () => void — apply a staged update now isSyncing, // download/verify in progress downloadProgress, // 0..1 isRestartRequired, // a bundle is staged, waiting for restart mandatoryUpdatePending, // a required update is waiting syncResult, // last result } = useOtaUpdate({ apiUrl: 'https://api.scalebun.com/api/v1', clientKey: 'skb_live_ck_…', appVersion: DeviceInfo.getVersion(), channelName: 'production', autoRestart: false, // let the user choose when to apply });
if (mandatoryUpdatePending) { return <BlockingUpdate progress={downloadProgress} />; }
if (isRestartRequired) { return <Banner text="Update ready" action="Restart" onPress={restart} />; }
return ( <Button title={isSyncing ? `Downloading ${Math.round(downloadProgress * 100)}%` : 'Check for updates'} disabled={isSyncing} onPress={sync} /> );}What the hook returns#
| Field | Type | Meaning |
|---|---|---|
sync() | () => Promise<SyncResult> | Check, download, verify and stage in one call |
restart() | () => void | Apply a staged bundle immediately |
isSyncing | boolean | A sync is in flight |
downloadProgress | number | 0–1 during download |
isRestartRequired | boolean | A bundle is staged and waiting for the next restart |
mandatoryUpdatePending | boolean | A required update is waiting |
activeBundle | OtaBundlePayload | null | The bundle currently running |
syncResult | SyncResult | null | The outcome of the last sync |
lastEvent | OtaEvent | null | The most recent OTA event |
SyncResult.status#
type SyncResult = { status: 'UP_TO_DATE' | 'UPDATE_INSTALLED' | 'ROLLED_BACK' | 'ERROR'; bundle?: OtaBundlePayload; error?: string;};UP_TO_DATE— nothing newer for this channel/version.UPDATE_INSTALLED— a bundle was downloaded and staged (applies on restart, or now ifautoRestart).ROLLED_BACK— the boot guard reverted a bad bundle.ERROR— seeerror.
3. When updates apply#
A staged bundle activates on the next app restart — never mid-session. You have three ways to control the moment:
Do nothing — it applies the next time the OS restarts your app.
Prompt — show
isRestartRequiredand callrestart()on the user's tap.Mandatory — mark the release mandatory (dashboard/CLI); with
mandatoryBlocksUithe SDK blocks until it's applied.
4. Listen to OTA events (optional)#
For analytics or custom UI, subscribe to the event stream instead of polling:
import { otaEventEmitter } from '@scalebun/react-native';
const off = otaEventEmitter.on('DOWNLOAD_PROGRESS', (e) => { console.log('progress', e);});// later: off();Next#
Publishing — build and ship the bundle this app will receive.
Channels & rollouts — control who gets it and when.
Signing — make sure only your bundles run.