Documentation

Ship OTA updates to your React Native app. Guides for installation, concepts, CLI, and SDK integration.

useBundleDrop

What useBundleDrop Is

useBundleDrop is the React hook for interacting with the initialized Bundle Drop runtime.

It gives you state plus explicit actions so you can build your own update UI, such as a "Check for updates" button, a download button, or a custom update prompt.

When to Use It

Use useBundleDrop when you want the app to decide how and when update actions happen from React UI.

This is the better fit when:

  • users trigger update checks manually
  • you want custom buttons, prompts, or settings screens
  • you want to switch channels from your own UI

Call BundleDrop.init first, then use the hook inside your screens or components.

Hook Options

useBundleDrop() takes no arguments.

The active channel comes from the runtime started by BundleDrop.init and can be changed later with setChannel(...).

What It Returns

  • status: latest human-readable status message
  • isEnabled: whether Bundle Drop is active for the current runtime
  • isBusy: whether an update action is currently running
  • channelName: active runtime channel used by OTA actions
  • setChannel(channelName): changes the active runtime channel
  • installedInfo: metadata for the installed bundle, if present
  • pendingApply: whether a downloaded bundle is waiting to be applied
  • hasBundle: whether a downloaded bundle exists locally
  • checkLatest(): checks for update availability
  • downloadUpdate(): downloads and stages an update
  • applyUpdate(): applies a staged update
  • fetchAvailableChannels(): fetches public channel names for the project
  • availableChannels: last fetched channel list
  • fetchBundles(options?): fetches a paginated list of downloadable bundles for a channel
  • installBundle(bundleListItem): downloads and stages a specific bundle from the list

Action Return Shapes

checkLatest() returns:

Promise<{
  response:
    | {
        action: "NOOP" | "INSTALL" | "ROLLBACK";
        upToDate?: boolean;
        bundleVersion?: number;
        version?: string;
        hash?: string;
        downloadUrl?: string;
        channelName?: string;
        reason?: string;
        incompatible?: boolean;
        runtimeVersion?: string;
        requestedRuntimeVersion?: string;
        latestRuntimeVersionOnChannel?: string;
      }
    | null;
  status?: string;
}>

downloadUpdate() returns:

Promise<{
  result:
    | { status: "staged"; bundlePath: string; hash: string }
    | { status: "upToDate" }
    | { status: "disabled" }
    | { status: "incompatible" }
    | { status: "rollback"; reason?: string };
  status?: string;
}>

applyUpdate() returns:

Promise<{
  result:
    | { status: "applied" }
    | { status: "noBundle" }
    | { status: "disabled" }
    | { status: "alreadyApplied" };
  status?: string;
}>

fetchBundles(options?) returns:

Promise<{
  items: BundleListItem[];
  nextCursor: string | null;
  hasMore: boolean;
}>

Each BundleListItem contains hash, bundleVersion, version, platform, runtimeVersion, releaseNotes, createdAt, and downloadUrl.

Options: channelName, platform, limit (1–50), cursor.

installBundle(bundleListItem) returns:

Promise<{
  result:
    | { status: "staged"; bundlePath: string; hash: string }
    | { status: "upToDate" }
    | { status: "disabled" }
    | { status: "incompatible" }
    | { status: "rollback"; reason?: string };
  status?: string;
}>

Typical Usage

UpdatesScreen.tsx
1const { 2 status, 3 channelName, 4 setChannel, 5 isBusy, 6 installedInfo, 7 pendingApply, 8 checkLatest, 9 downloadUpdate, 10 applyUpdate, 11} = useBundleDrop(); 12 13setChannel("Beta"); 14 15// Example flow: 16// 1. User taps "Check for updates" 17// 2. User taps "Download" 18// 3. User taps "Apply now" or waits for next launch

Bundle Browsing Usage

VersionPickerScreen.tsx
1const { 2 channelName, 3 setChannel, 4 isBusy, 5 status, 6 fetchBundles, 7 installBundle, 8 applyUpdate, 9} = useBundleDrop(); 10 11setChannel("production"); 12 13// Fetch available bundles 14const page = await fetchBundles({ limit: 10 }); 15// page.items = [{ hash, bundleVersion, version, releaseNotes, ... }] 16 17// Load more 18const nextPage = await fetchBundles({ cursor: page.nextCursor }); 19 20// User picks a version from the list 21const result = await installBundle(page.items[2]); 22if (result.result.status === "staged") { 23 await applyUpdate(); // reload with the selected version 24}

Bundle Drop handles the download, staging, and installation. Your app only needs to render the list and handle user selection.

How It Differs from BundleDrop.init

The difference is mostly about control:

  • BundleDrop.init is the required startup controller
  • useBundleDrop is the React helper for manual, UI-driven behavior on top of that runtime

You can still use the same underlying Bundle Drop integration either way. The difference is whether the app handles updates automatically at startup or exposes that flow through your own interface.

Best Fit

useBundleDrop is best for:

  • custom update screens
  • settings-based update controls
  • apps that want explicit user interaction around updates

Related Docs