npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rollbird/react-native

v0.1.2

Published

React Native client for Rollbird hosted OTA updates.

Downloads

115

Readme

@rollbird/react-native

A thin wrapper over @hot-updater/react-native's HotUpdater.wrap() that points your app at Rollbird and reports install / failure beacons back to your project's dashboard.

This package only wraps the client side. Run npx rollbird init (from @rollbird/cli) first — it detects your project, creates or links a Rollbird project, generates a signing key pair for it server-side, and writes the channel + public key into your native config. rollbird release then builds, signs, and publishes the bundles this wrapper checks for and downloads.

Install

npm i @rollbird/react-native @hot-updater/react-native

@hot-updater/react-native is a peer dependency pinned to 0.36.13 — install that exact version alongside this package (rollbird init does this for you).

Usage

import { Rollbird } from "@rollbird/react-native";
import App from "./App";

export default Rollbird.wrap({
  projectKey: "proj_xxx",
})(App);

projectKey is the only required option. Everything else has a sane default:

| Option | Default | Notes | | --- | --- | --- | | apiUrl | https://ota.rollbird.dev | Development only (pointing at a local pnpm dev API). Production apps leave it alone: the endpoint ships with the SDK and does not change. | | strategy | "fingerprint" | Or "appVersion". | | fallbackComponent | none | Rendered with { progress, status } while an update downloads. | | onProgress | none | Called with { progress } on every download tick. | | onUpdateProcessCompleted | none | Called with the raw Hot Updater result. | | onNotifyAppReady | none | Called with Hot Updater's launch report ({ status: "RECOVERED" \| "STABLE", crashedBundleId? }). | | onError | none | Called with the error that aborted the update. | | reloadOnForceUpdate | true | Passed through to Hot Updater. | | requestTimeout | Hot Updater default | Passed through to Hot Updater. | | requestHeaders | none | Passed through to Hot Updater. |

Expo (CNG)

expo prebuild regenerates ios/ and android/ from scratch, which deletes anything rollbird init writes into a bare project's native config — the app keeps running and simply stops checking for updates. This package ships an Expo config plugin that puts it back on every prebuild, locally and on EAS Build. npx rollbird init registers it for you; to do it by hand, add it to app.json:

{
  "expo": {
    "plugins": [["@rollbird/react-native", { "channel": "production" }]]
  }
}

channel is the only prop. Everything else is resolved at prebuild time, in this order:

  1. the plugin prop (channel only),
  2. channel / publicKeyPem / fingerprints.<platform> in rollbird.json, written by rollbird init,
  3. the ROLLBIRD_CHANNEL / ROLLBIRD_PUBLIC_KEY / ROLLBIRD_FINGERPRINT_IOS / ROLLBIRD_FINGERPRINT_ANDROID environment variables.

The channel falls back to production. The public key and the fingerprint have no fallback: a build that can't verify a bundle signature would install updates it cannot authenticate, and one with the wrong fingerprint silently never updates — so the plugin fails the prebuild instead, naming both fixes (npx rollbird init, or the EAS secrets below).

What it writes into the freshly generated native project:

| Platform | Where | Keys | | --- | --- | --- | | iOS | Info.plist | HOT_UPDATER_CHANNEL, HOT_UPDATER_PUBLIC_KEY, HOT_UPDATER_FINGERPRINT_HASH | | Android | AndroidManifest.xml meta-data | com.hotupdater.CHANNEL, com.hotupdater.PUBLIC_KEY, com.hotupdater.FINGERPRINT_HASH | | both | AppDelegate / MainApplication | Hot Updater's own entry-point patch, so a downloaded bundle actually launches |

Every value is read, never computed. rollbird init computes the Expo fingerprint once per platform and stores it in rollbird.json; the plugin stamps that exact string into the build and rollbird release tags releases with the same one, so a binary and its updates can't disagree. The plugin must not compute its own: @expo/fingerprint hashes an extra dir:android (and dir:ios) source as soon as those directories exist, so on a machine that has prebuilt locally — which is every machine, right after this plugin runs — it returns a different value than a clean checkout does, and the binary would carry a fingerprint no release ever matches. Re-run npx rollbird init after changing native dependencies to refresh the stored value, then prebuild and rebuild.

Android values go in manifest meta-data, not strings.xml, because that's what the native module reads first — and because Hot Updater's own plugin deletes those string resources.

EAS Build

rollbird.json carries your project id and key, so many teams gitignore it. On a build worker where the file is absent, pass the values as secrets:

eas env:create --name ROLLBIRD_PUBLIC_KEY --value "$(node -p "require('./rollbird.json').publicKeyPem")"
eas env:create --name ROLLBIRD_CHANNEL --value production
eas env:create --name ROLLBIRD_FINGERPRINT_IOS --value "$(node -p "require('./rollbird.json').fingerprints.ios")"
eas env:create --name ROLLBIRD_FINGERPRINT_ANDROID --value "$(node -p "require('./rollbird.json').fingerprints.android")"

The plugin reads them exactly like the file. (EAS Build itself is documented but not yet verified end to end by Rollbird.)

Requirements

  • @expo/config-plugins (any version from 9 up, i.e. Expo SDK 52+) — already in every Expo project; it's an optional peer dependency here so bare projects don't have to install it.
  • @expo/fingerprint — used by rollbird init and rollbird release, not by the plugin; npx expo install @expo/fingerprint if init says it can't be loaded.
  • expo-updates cannot be installed. It owns the same update process Hot Updater does: both swap the JS bundle at launch, and the last one to win decides which bundle runs. rollbird init refuses to set up a project that has it. Remove expo-updates (and its expo.updates config) before adding Rollbird.
  • Expo Go can't run custom native code, so updates only work in a development build or a release build.

Verified on Expo SDK 53 (RN 0.79) and SDK 57 (RN 0.86), New Architecture on.

Installing from a path, not npm

If you depend on this package by path — file:../packages/react-native in a monorepo, or npm link — that install brings its devDependencies with it, including the React Native copy kept here for tests. Metro then resolves react-native for this package's modules from that copy instead of your app's, and your app bundles two React Natives. The older one's TurboModuleRegistry looks up a global that newer React Native no longer defines, so every native module lookup returns null and the app dies on launch with TurboModuleRegistry.getEnforcing('HotUpdater') could not be found — an error that says nothing about resolution.

Installing from npm (or a packed tarball) avoids it entirely, since devDependencies aren't installed and react-native stays a peer. If you must link by path, make react and react-native singletons in your metro.config.js by resolving them from the app root.

Don't register @hot-updater/react-native's own config plugin as well. Rollbird's writes everything it would (Hot Updater's native module is autolinked from your dependencies either way), and the two take their values from different places — Hot Updater's from hot-updater.config.ts and its local signing keys. With both registered the plugin listed first wins (mods registered first run last), so the plugin warns when it sees them together.

How it works

Rollbird.wrap does no update logic of its own. It builds a baseURL (<apiUrl>/v1/ota/<projectKey>) and an updateStrategy, then hands both to the open-source Hot Updater client's own HotUpdater.wrap() — so bundle resolution, download, and apply/reload behavior are exactly upstream Hot Updater's, unmodified. Rollbird's hosted API implements the same check contract Hot Updater's OSS backends do, and adds channels, staged rollout, and rollback on top; this wrapper adds only the apiUrl/projectKey plumbing and the beacon reporting below.

The native config HotUpdater.wrap() reads at runtime (release channel, public key for signature verification) is written by rollbird init, not by this package — see @rollbird/cli.

Beacons

Rollbird.wrap posts a best-effort, fire-and-forget beacon to ${apiUrl}/v1/events for:

  • download_complete — once per bundle, when the offered bundle finishes downloading (it runs on the next launch). It carries the release id taken from the update check, so a background download that ends after Hot Updater reports the process complete is still attributed. Rollbird.wrap passes Hot Updater a resolver that wraps createDefaultResolver(baseURL) to read that id; requests are unchanged.
  • install_success — immediately after download_complete, once for the same offered bundle. It uses the same release id and signed offer receipt. The install metric therefore records a successfully downloaded and staged bundle; activation happens on the next launch.
  • install_failure — the app launched RECOVERED after a bundle crashed (carries the crashed bundle id), or the update process errored (no release id, since none is known at that point).

Beacon requests never throw and never block your callbacks — a failed POST is swallowed silently and your own onProgress / onUpdateProcessCompleted / onNotifyAppReady / onError callbacks are always still invoked with the original arguments.

The server counts a beacon only when it carries the short-lived signed offer receipt returned by the update check. That proves the event refers to an offer Rollbird issued; it is not hardware/device attestation. A crash reported after the process restarts currently has no in-memory receipt and is intentionally dropped rather than counted as trusted telemetry.

Passthrough

Rollbird.checkForUpdate, Rollbird.runUpdateProcess, Rollbird.getBundleId, Rollbird.getChannel, Rollbird.getAppVersion, and Rollbird.reload are the same functions exported by @hot-updater/react-native's HotUpdater, so you don't need to import both packages for manual update flows.