From a JavaScript fix to a verified update on users’ devices.

NitroPush separates the native app-store release from the JavaScript update lane. Your binary provides the trusted runtime; NitroPush safely delivers compatible bundles between store releases.

Last reviewed August 29, 2026

Choose the SDK for your framework

The walkthrough below uses React Native and Expo. NativeScript has a separate alpha integration with its own native startup hook and activation rules.

The two release lanes

React Native applications contain native code and a JavaScript bundle. NitroPush updates the JavaScript bundle and compatible assets without replacing the signed iOS or Android binary. Changes to Swift, Kotlin, permissions, native modules, entitlements, or the app’s core purpose still require a new App Store or Google Play release.

Native release lane

Use the stores for native dependencies, operating-system permissions, runtime compatibility changes, and new binary versions.

OTA release lane

Use NitroPush for compatible JavaScript, TypeScript, UI, business-logic, copy, and asset fixes between native releases.

1. Install the runtime once

Add @nitropush/react-native to the app. Expo projects use the NitroPush config plugin; bare React Native projects wire the native bundle resolver into AppDelegate and MainApplication. Debug builds continue to use Metro, while release builds can load the active verified OTA bundle.

The deployment key belongs in the native configuration—not a public JavaScript environment variable. It identifies the project environment the binary is allowed to query.

2. Configure before React renders

The hosted setup uses the no-argument configure() function at module scope. After the first successful render, notifyAppReady() confirms that the active bundle is healthy. A later sync() call checks for and installs a compatible release.

import { configure, sync, InstallMode } from '@nitropush/react-native';
import { useEffect } from 'react';

// Module scope: initialize before React renders.
const client = configure();

export function App() {
  useEffect(() => {
    client.notifyAppReady();
    sync(client, { installMode: InstallMode.ON_NEXT_RESUME });
  }, []);

  return null;
}
Why confirmation matters: a newly activated bundle remains unconfirmed until notifyAppReady() runs. If the next launch finds an unconfirmed bundle, NitroPush restores the previous known-good version.

3. Build and upload a release

The CLI accepts Expo export directories and CodePush-style Hermes or JavaScript bundles. It determines the artifact kind, computes SHA-256 checksums, uploads the required bytes, and creates a release scoped to a project and environment.

nitropush release upload \
  --project <project-id> \
  --environment prod \
  --runtime-version 1.2.3 \
  --label 1.2.4 \
  --bundle-path ./dist

--runtime-version protects runtime compatibility: a release for one native app version is not blindly offered to another. Test, stage, and production use separate environments and deployment keys.

4. Sign before the upload leaves CI

Projects can register an ECDSA P-256 public key. The private key stays with your team and signs the bundle during upload. Devices verify the signature before activation, so a modified or unsigned payload is rejected. Keep the private PEM in a secret manager and pass its temporary file path with --signing-key.

5. Route the right release to the right device

When the SDK checks for an update, NitroPush evaluates the deployment key, platform, app version, release status, and rollout bucket. A production device cannot accidentally receive a staging release, and percentage rollouts use a stable device identity so a device remains in the same bucket.

  1. The application calls sync() on foreground, on demand, or from your chosen lifecycle event.
  2. The SDK requests the latest compatible release.
  3. NitroPush returns no update or a manifest for the matching full bundle or delta patch.
  4. The SDK downloads in the background and reports progress.

6. Download fewer bytes when a delta is available

With delta updates enabled, NitroPush can create a bsdiff patch between compatible bundles. A device reports the hash of its current bundle; the service returns a matching patch only when one exists and is meaningfully smaller. The complete bundle remains available for fresh installs, mismatched bases, and fallback.

After applying a patch, the SDK verifies the reconstructed bundle’s SHA-256 checksum and then verifies its cryptographic signature when signing is enabled.

7. Install on your schedule

Choose an install mode that matches the urgency and user experience: immediate activation, next restart, next resume, or next suspension. Normal updates can wait for a natural lifecycle boundary; critical JavaScript fixes can use a more urgent policy after testing.

8. Observe, expand, or recover

Download, install, failure, rollback, and device-activity events originate in the native SDK, including events that JavaScript cannot reliably report during a failed launch. Start with a limited rollout, observe the release, then increase the percentage. If a bundle fails before confirmation, the SDK restores the last known-good bundle on the next launch.

What NitroPush deliberately does not do

  • It does not bypass store review for native code changes.
  • It does not make an incompatible JavaScript/native API contract safe.
  • It does not require production apps to load bundles in debug builds.
  • It does not put your private bundle-signing key on NitroPush servers.

Continue with the React Native OTA update guide, compare the approach with CodePush and EAS Update, or follow the release documentation.