How to Integrate NitroPush with React Native and Expo
Install the NitroPush SDK, configure Expo or bare React Native, confirm healthy launches, sync updates, sign bundles, and upload your first OTA release.
NitroPush delivers compatible JavaScript bundles and assets to installed React Native and Expo applications. This guide covers the hosted NitroPush path: native configuration supplies the deployment key, JavaScript calls the no-argument configure() function, and the CLI publishes releases.
For the system-level explanation first, read how NitroPush works.
Prerequisites
- A React Native or Expo application with iOS and/or Android release builds
- Node.js and the native toolchains required by your project
- A NitroPush account and organization
- A project ID and deployment key for a test, stage, or production environment
1. Install the SDK
npm install @nitropush/react-native react-native-nitro-modules
# or
yarn add @nitropush/react-native react-native-nitro-modules
The same JavaScript package supports Expo and bare React Native. Native wiring differs by project type.
2. Create a project and environment
Install the CLI and sign in:
npm install -g nitropush
nitropush login
nitropush whoami --json
Create an application. --org is optional when login already saved a default organization, but using it explicitly makes setup scripts easier to audit.
nitropush app create --org <org-id> --name "My App"
nitropush env create --app <project-id> --name prod
Save the real deployment key printed by env create. Never invent one or expose it through a public JavaScript environment variable.
3A. Expo: use the config plugin
Use a dynamic Expo app config so the plugin can read the deployment credential from a private build environment variable without committing it or exposing it to JavaScript:
// app.config.js
module.exports = {
"expo": {
"plugins": [
[
"@nitropush/react-native",
{
"deploymentKeyEnvVar": "NITROPUSH_DEPLOYMENT_KEY",
"ios": true,
"android": true
}
]
]
}
};
deploymentKeyEnvVar defaults to NITROPUSH_DEPLOYMENT_KEY. Set that private
variable in the environment that runs local prebuilds and CI/EAS builds. Do not
prefix it with EXPO_PUBLIC_, because Expo embeds public variables in the
JavaScript bundle.
Then regenerate and rebuild the native projects:
npx expo prebuild
The plugin writes the deployment key into native configuration and injects the native bundle-resolution hooks. A native rebuild is required; reloading Metro is not enough. The same is true when enabling or disabling Delta Updates: change enableDeltaUpdates in the plugin, run prebuild, and rebuild the native app.
3B. Bare React Native: wire the native hosts
Bare applications set NITROPUSH_DEPLOYMENT_KEY in Info.plist and the Android <application> metadata, install the SDK early in application startup, and serve the active NitroPush bundle only in release builds.
The exact AppDelegate.swift and MainApplication.kt structure varies across React Native versions. Use the maintained snippets in the bare React Native installation guide instead of copying an old AppDelegate template.
Two safeguards are mandatory:
- Debug builds must continue to use Metro.
- Release builds must fall back to the binary-shipped bundle when no valid OTA bundle exists.
After iOS changes, install pods and rebuild the native applications.
4. Configure at module scope
For hosted NitroPush, configure() takes no arguments. It reads the deployment key from native configuration and initializes the native singleton before React renders.
import {
configure,
sync,
InstallMode,
SyncStatus,
} from '@nitropush/react-native';
const client = configure();
Do not move configure() into a component or useEffect. Use configureWith() only for an explicitly self-hosted server or custom CDN configuration.
5. Confirm the first healthy render
notifyAppReady() tells the rollback safety system that the active bundle reached a known-good state.
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
client.notifyAppReady().catch((error) => {
console.error('NitroPush app-ready confirmation failed', error);
});
}, []);
return <YourApplication />;
}
Skipping this call can make a healthy update appear unconfirmed and trigger recovery behavior on the next launch.
6. Check for updates
Call sync() at the lifecycle point that matches your product experience—for example app foreground, after login, or from a manual settings button.
await sync(
client,
{ installMode: InstallMode.ON_NEXT_RESUME },
(status, error) => {
if (status === SyncStatus.UNKNOWN_ERROR) {
console.error('NitroPush sync failed', error);
}
},
({ receivedBytes, totalBytes }) => {
console.log(`${receivedBytes} / ${totalBytes}`);
},
);
Concurrent sync requests for the same client share the in-flight operation instead of racing.
7. Enable bundle signing for production
Generate an ECDSA P-256 keypair and register the public key:
nitropush app signing-key generate \
--app <project-id> \
--out ./nitropush-signing.pem
PEM files are ignored by this repository’s secret rules, but you should still store the production private key in a secret manager. Never pass PEM contents directly on a command line. Write the secret to a temporary file in CI and pass the path with --signing-key.
8. Upload the first release
Build or export your bundle, then publish it with explicit targeting:
nitropush release upload \
--project <project-id> \
--environment prod \
--runtime-version 1.0.0 \
--label 1.0.1 \
--bundle-path ./dist \
--signing-key ./nitropush-signing.pem
The CLI detects Expo export metadata or a CodePush-style Hermes/JavaScript bundle. If signing is not configured for the project, omit --signing-key. Add --delta only after validating full-bundle delivery and the delta path in a non-production environment.
9. Verify the failure path
Before production, test more than a successful download:
- Confirm debug builds still load Metro.
- Confirm a release build starts from the embedded bundle.
- Publish a staging update and verify download, activation, and
notifyAppReady(). - Verify an invalid signature is rejected in an isolated test environment.
- Verify an unconfirmed bundle returns to the previous known-good bundle.
- Start with a limited rollout and expand only after observing real-device outcomes.