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

react-native-codepush-sdk

v1.1.4

Published

A React Native CodePush SDK for over-the-air updates

Readme

React Native CodePush SDK

A React Native SDK for over-the-air (OTA) updates with support for your own backend. Use it to ship JS and asset updates without going through the app stores.

CodePush Portal (Dashboard) — manage deployments and view update status.

  • Custom server: Works with any server that implements the CodePush-style API.
  • Unified API: Same API on iOS and Android (provider, hook, or class-based).
  • Optional demo: See the example/ app for a full setup.

ZIP + manifest mode (for large bundle)

In addition to the classic CodePush-style API (/update_check), the SDK supports a ZIP + manifest mode. This is useful when you host a single large bundle (e.g. for a social app) and want clients to decide "new version or not" from a tiny JSON manifest, without downloading the ZIP on every check.

Manifest format

Host a small JSON file (for example at https://your-server.com/codepush/manifest.json).

You can either use a single ZIP for both platforms:

{
  "packageHash": "abc123def456",
  "zipBundleUrl": "https://your-server.com/codepush/bundles/app-abc123def456.zip",
  "size": 104857600
}

or a platform-specific ZIP per OS, with one manifest URL and both URLs inside:

{
  "packageHash": "abc123def456",
  "zipBundleUrlAndroid": "https://your-server.com/codepush/bundles/app-android.zip",
  "zipBundleUrliOS": "https://your-server.com/codepush/bundles/app-ios.zip",
  "sizeAndroid": 104857600,
  "sizeiOS": 104857600
}
  • packageHash: logical version / hash of the OTA bundle.
  • zipBundleUrl: public URL of the ZIP bundle when using a single ZIP.
  • zipBundleUrlAndroid / zipBundleUrliOS: platform-specific ZIP URLs (when using separate bundles).
  • size, sizeAndroid, sizeiOS (optional): used for progress UI and basic size checks.

Generating manifest.json in your build/CI

This repo provides 2 scripts at the repo root to build the OTA ZIP and update codepush/manifest.json.

Run from the repo root:

# iOS
bash build-codepush.sh ios
node build-manifest.js ios

# Android
bash build-codepush.sh android
node build-manifest.js android

Optional: add a package.json script for one-liner usage (example for iOS):

{
  "scripts": {
    "codepush:ios": "bash scripts/build-codepush.sh ios && node scripts/build-manifest.js ios"
  }
}

Then run:

yarn codepush:ios

This produces:

  • codepush/<platform>/index.bundle + assets + metadata.json
  • codepush_<platform>_release.zip at the repo root
  • codepush/manifest.json (unified manifest with packageHash + platform URL/size)

You can adapt the hashing scheme (e.g. include assets or use a git commit hash) as long as packageHash stays stable for a given bundle and changes when you release a new version.

Using CustomCodePush with a manifest

You can keep serverUrl and deploymentKeyIOS / deploymentKeyAndroid for the classic API, or omit them if you only use the manifest-based flow. On app startup (or whenever you want to check), call:

import CustomCodePush from 'react-native-codepush-sdk';

const codePush = new CustomCodePush({
  // Optional: serverUrl and deploymentKeyIOS / deploymentKeyAndroid for classic mode.
  // Optional: manifestUrl if you want to store it in config.
  manifestUrl: EnvConfig.codePushManifestUrl,
  installMode: 'ON_NEXT_RESTART',
});

await codePush.initialize();

const result = await codePush.checkAndInstallFromManifest({
  manifestUrl: EnvConfig.codePushManifestUrl,
  installMode: 'ON_NEXT_RESTART', // or 'IMMEDIATE' / 'ON_NEXT_RESUME'
});

console.log('Has new version:', result.hasNewVersion);
console.log('Installed hash:', result.installedHash);
console.log('Latest hash:', result.latestHash);

checkAndInstallFromManifest will:

  • Fetch the manifest (small JSON).
  • Compare manifest.packageHash with the currently installed packageHash.
  • Download and install the ZIP from zipBundleUrl only when the hash changes.
  • Treat "no current package hash" as first install and install once.

This design works well for social-style apps or any scenario where you distribute one large bundle, minimize per-launch network traffic, and still get OTA behavior.

Features

  • Custom server – Point to your own update server (see Backend).
  • OTA updates – Download and install JS bundles and assets without a store release.
  • Rollback – Revert to the previous bundle on failed install.
  • Progress – Download and install progress callbacks.
  • Mandatory updates – Optional forced updates.
  • Install modes – Immediate, on next restart, or on next resume.
  • Gradual rollouts – Server-side rollout percentage support.

Requirements

  • React Native >= 0.60, React >= 16.8
  • Node >= 18 (for tooling)

Installation

1. Install the package

npm install react-native-codepush-sdk

Peer dependencies (install if not already present):

npm install react-native-fs react-native-zip-archive react-native-device-info @react-native-async-storage/async-storage react-native-restart 

2. Platform setup

iOS

  1. Run cd ios && pod install.
  2. If your server uses HTTP (e.g. local dev), add to Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Android

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Usage

App version is read from the device via react-native-device-info; you configure serverUrl and platform keys (deploymentKeyIOS, deploymentKeyAndroid).

Provider + component (recommended)

import React from 'react';
import { SafeAreaView } from 'react-native';
import { CodePushProvider, UpdateChecker } from 'react-native-codepush-sdk';

const config = {
  serverUrl: 'https://your-codepush-server.com',
  deploymentKeyIOS: 'your-ios-key',
  deploymentKeyAndroid: 'your-android-key',
  checkFrequency: 'ON_APP_START',
  installMode: 'ON_NEXT_RESTART',
  minimumBackgroundDuration: 0,
};

export default function App() {
  return (
    <CodePushProvider config={config} autoCheck checkOnResume>
      <SafeAreaView style={{ flex: 1 }}>
        <UpdateChecker />
      </SafeAreaView>
    </CodePushProvider>
  );
}

useCodePush hook

import { View, Button } from 'react-native';
import { useCodePush } from 'react-native-codepush-sdk';

function MyScreen() {
  const {
    availableUpdate,
    isChecking,
    checkForUpdate,
    syncUpdate,
    getBundleUrl,
  } = useCodePush();

  return (
    <View>
      {availableUpdate && (
        <Button title="Install Update" onPress={syncUpdate} />
      )}
      <Button
        title="Check for Updates"
        onPress={checkForUpdate}
        disabled={isChecking}
      />
    </View>
  );
}

Class-based (CustomCodePush)

import { CustomCodePush } from 'react-native-codepush-sdk';

const codePush = new CustomCodePush({
  serverUrl: 'https://your-server.com',
  deploymentKeyIOS: 'your-ios-key',
  deploymentKeyAndroid: 'your-android-key',
});

await codePush.initialize();

const update = await codePush.checkForUpdate();
if (update) {
  const localPackage = await codePush.downloadUpdate(update, (progress) => {
    console.log(`${progress.receivedBytes}/${progress.totalBytes}`);
  });
  await codePush.installUpdate(localPackage);
}

What you use and receive

You configure: serverUrl, deploymentKeyIOS, deploymentKeyAndroid (and optionally installMode, checkFrequency). The SDK automatically sends bundleId (from the device; not configurable) with every API request for backend validation.

You call: checkForUpdate(), syncUpdate(), rollback(), clearUpdates(), getBundleUrl() (from useCodePush() or CustomCodePush).

You receive: availableUpdate (when an update exists), currentUpdate (currently installed), syncStatus, isChecking, isDownloading, isInstalling. The SDK handles the rest (network calls, install, reporting) internally.


Backend (for server implementers)

Your server must expose these routes under serverUrl (e.g. on Vercel): POST /api/v1/update_check, POST /api/v1/report_status/deploy, POST /api/v1/report_status/download. The SDK calls them automatically. Request and response formats are documented in src/api/server-example.md.

Configuration

interface CodePushConfiguration {
  serverUrl?: string;           // Base URL of your update server (no trailing slash)
  deploymentKeyIOS?: string;    // iOS deployment key (e.g. from CMS dashboard)
  deploymentKeyAndroid?: string; // Android deployment key (e.g. from CMS dashboard)
  /**
   * Optional URL to a small JSON manifest that describes the latest bundle
   * (for ZIP+manifest mode, e.g. large/social apps).
   */
  manifestUrl?: string;
  checkFrequency?: 'ON_APP_START' | 'ON_APP_RESUME' | 'MANUAL';  // default: 'ON_APP_START'
  installMode?: 'IMMEDIATE' | 'ON_NEXT_RESTART' | 'ON_NEXT_RESUME';  // default: 'ON_NEXT_RESTART'
  minimumBackgroundDuration?: number;  // default: 0 (seconds in background before resume check)
}

(SDK sends bundleId automatically from the device; it is not in config.)

Minimum API (core surface)

  • Config: serverUrl, deploymentKeyIOS, deploymentKeyAndroid (optional: installMode, checkFrequency).
  • React: CodePushProvider + useCodePush() (or class-based CustomCodePush).
  • Call: checkForUpdate(), then syncUpdate() (or downloadUpdate + installUpdate), and getBundleUrl() for the bundle to load.
  • Get: getCurrentPackage() / getUpdateMetadata(), and recovery via rollback().

Bundle loading

Use getBundleUrl() from useCodePush() or CustomCodePush.getBundleUrl() for the OTA bundle path. The SDK stores updates under a hash-based path; BundleManager.installBundle(sourcePath) copies the active bundle to CustomCodePush/current.bundle so native can load it on next launch.

To load that file at app start you must configure the native JS bundle entrypoint (Bare workflow or Expo custom dev client). The following config is based on the working example in this repo (example/AppDelegate.swift, example/MainApplication.kt).

Native config (iOS)

Use a delegate that overrides bundleURL() and prefers CustomCodePush/current.bundle when it exists and is not empty (so you can test OTA in Debug too):

override func bundleURL() -> URL? {
  let fileManager = FileManager.default
  let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
  let customBundleURL = documentsDirectory.appendingPathComponent("CustomCodePush/current.bundle")

  var isDirectory: ObjCBool = false
  let exists = fileManager.fileExists(atPath: customBundleURL.path, isDirectory: &isDirectory)
  let isNotEmpty = (try? fileManager.attributesOfItem(atPath: customBundleURL.path)[.size] as? Int64 ?? 0) ?? 0 > 0

  if exists && !isDirectory.boolValue && isNotEmpty {
    return customBundleURL
  }

#if DEBUG
  return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
#else
  return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}

(With Expo, implement this in your ExpoReactNativeFactoryDelegate subclass and return it from sourceURL(for:) as bridge.bundleURL ?? bundleURL().)

Native config (Android)

In your ReactNativeHost, override getJSBundleFile() and optionally getUseDeveloperSupport() so a valid OTA bundle is used even in debug:

override fun getJSBundleFile(): String? {
  val file = File(applicationContext.filesDir, "CustomCodePush/current.bundle")
  return if (file.exists() && file.length() > 0) file.absolutePath else super.getJSBundleFile()
}

override fun getUseDeveloperSupport(): Boolean {
  val file = File(applicationContext.filesDir, "CustomCodePush/current.bundle")
  val exists = file.exists() && file.length() > 0
  return if (exists) false else BuildConfig.DEBUG
}

After download + install

Copy the installed update to current.bundle so the native entrypoint loads it on next launch:

import { BundleManager } from 'react-native-codepush-sdk';

// After installUpdate(localPackage)
await BundleManager.installBundle(localPackage.localPath);

See the example/ app for full iOS and Android setup.

API Reference

Core exports

  • CodePushProvider – React context provider; wrap your app (or root screen) with it.
  • useCodePush – Hook to access update state and actions (must be used inside CodePushProvider).
  • CustomCodePush – Class for non-React or manual integration.
  • Types: CodePushConfiguration, UpdatePackage, LocalPackage, SyncStatus, plus types/codepush (CodePushUpdate, CodePushDeployment, CodePushSyncStatus, UpdateHistory, etc.).

Optional exports

  • UpdateChecker – UI component for update prompts and actions.
  • BundleManager – Utility for custom bundle paths (prefer getBundleUrl() for OTA).
  • useFrameworkReady – Hook for framework/engine ready (e.g. Hermes).

CustomCodePush

  • initialize(): Promise that resolves when SDK is ready (directories + stored package loaded).
  • checkForUpdate(): Returns UpdatePackage | null.
  • downloadUpdate(update, progressCallback?): Returns Promise<LocalPackage>.
  • installUpdate(localPackage): Install a downloaded update.
  • sync(statusCallback?, progressCallback?): Check, download, and install in one flow.
  • getCurrentPackage(): Current installed package or null.
  • getBundleUrl(): URL/path to load the current bundle (e.g. for custom loaders).
  • rollback(): Rollback to the previous version.
  • clearUpdates(): Clear all downloaded updates.

CodePushProvider

  • config (CodePushConfiguration): Required.
  • autoCheck (boolean, default true): Run a check on mount.
  • checkOnResume (boolean, default true): Run a check when app comes to foreground.

useCodePush()

Returns: codePush, currentUpdate, availableUpdate, syncStatus, isChecking, isDownloading, isInstalling, checkForUpdate, syncUpdate, rollback, clearUpdates, getBundleUrl.

Update package format

The server serves a ZIP (with index.bundle, optional assets/, metadata.json) or a single JS file. Details and metadata layout: src/api/server-example.md.

Development and example app

  • Example app: example/ uses this SDK; run with cd example && npm start. Point serverUrl at your own backend (e.g. dashboard on Vercel).
  • Test script: node test-update-check.js (with your backend running on the URL and port used in the script).

Security

  • Prefer HTTPS for the update server.
  • Validate deployment keys and apply rate limiting on the server.
  • Consider signed packages and automatic rollback on failed installs.

Troubleshooting

  • Network errors: Confirm serverUrl and device connectivity (and ATS/cleartext if using HTTP).
  • Permission errors: Ensure storage permissions (e.g. Android manifest) are set.
  • Bundle/install failures: Check package format, integrity, and free space.

License

MIT – see LICENSE.

Links