For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /codepush-api-migration.md.

react-native-code-push to Cresc API migration guide

Microsoft retired App Center, and CodePush with it, on March 31, 2025. If your app still calls codePush.sync() or wraps its root component with codePush(), this page maps each CodePush API, option and CLI command to its Cresc equivalent so you can port the integration line by line.

Cresc ships as the react-native-update package. The release model is the one you already know: upload the native build users have installed, then publish JavaScript updates against it. For the project-level plan (auditing apps, CI, rollout), use the migration checklist; this page covers the code.

1. Swap the native bundle URL

CodePush made the app load its downloaded bundle by replacing the bundle URL. Cresc works the same way, so the native change is a one-line swap.

iOS (AppDelegate or, from RN 0.88, SceneDelegate):

// Before
CodePush.bundleURL()

// After
import react_native_update
RCTPushy.bundleURL()

Android (MainApplication):

// Before
override fun getJSBundleFile(): String = CodePush.getJSBundleFile()

// After
import cn.reactnative.modules.update.UpdateContext

override fun getJSBundleFile(): String? = UpdateContext.getBundleUrl(this@MainApplication)

On RN 0.82 and later the Android template passes jsBundleFilePath = UpdateContext.getBundleUrl(this) to getDefaultReactHost instead. Installation & Config has the exact snippet for every RN version, plus Expo.

Remove the CodePush deployment key from Info.plist and strings.xml. Cresc reads its appKey from JavaScript instead.

2. Replace the root wrapper

// Before: react-native-code-push
import codePush from "react-native-code-push";

const App = () => <Root />;

export default codePush({
  checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
  installMode: codePush.InstallMode.ON_NEXT_RESTART,
})(App);
// After: Cresc
import { Platform } from "react-native";
import { Cresc, UpdateProvider } from "react-native-update";
import updateConfig from "./update.json";

const cresc = new Cresc({
  appKey: updateConfig[Platform.OS].appKey,
  checkStrategy: "onAppResume",
  updateStrategy: "silentAndLater",
});

export default function App() {
  return (
    <UpdateProvider client={cresc}>
      <Root />
    </UpdateProvider>
  );
}

update.json is written by cresc createApp or cresc selectApp and holds one appKey per platform, the way CodePush had one deployment key per app. See Code Integration.

3. Option mapping

CodePush optionCresc option
deploymentKeyappKey (one per platform, from update.json)
checkFrequency: ON_APP_STARTcheckStrategy: "onAppStart"
checkFrequency: ON_APP_RESUMEcheckStrategy: "onAppResume"
(start and resume)checkStrategy: "both" (the default)
checkFrequency: MANUALcheckStrategy: null, then call checkUpdate() yourself
installMode: IMMEDIATEupdateStrategy: "silentAndNow"
installMode: ON_NEXT_RESTARTupdateStrategy: "silentAndLater"
installMode: ON_NEXT_RESUMEupdateStrategy: null, then call switchVersion() from an AppState listener
updateDialog: trueupdateStrategy: "alwaysAlert" or "alertUpdateAndIgnoreError"
updateDialog text overridesupdateStrategy: null and render your own UI with useUpdate()
codePush.disallowRestart()beforeReload returning false cancels a pending reload

All options are listed in the API reference.

4. Method mapping

useUpdate() must be called from a component rendered inside <UpdateProvider>.

CodePushCresc (useUpdate() unless noted)
codePush.sync()The built-in flow driven by checkStrategy and updateStrategy
codePush.checkForUpdate()checkUpdate(), result in updateInfo
remotePackage.download(progressCallback)downloadUpdate(), progress in progress or useUpdateProgress()
localPackage.install(InstallMode.IMMEDIATE)switchVersion()
localPackage.install(InstallMode.ON_NEXT_RESTART)switchVersionLater()
codePush.notifyAppReady()Automatic (autoMarkSuccess: true); call markSuccess() if you turn it off
codePush.restartApp()restartApp()
codePush.getUpdateMetadata()currentVersionInfo and currentHash
codePush.clearUpdates()resetToPackagedBundle()
remotePackage.isMandatoryPut a flag in the version's metaInfo (pattern)
remotePackage.descriptionupdateInfo.description

A manual check-download-install flow that used to be built on checkForUpdate looks like this in Cresc:

import { useUpdate } from "react-native-update";

function UpdateButton() {
  const { checkUpdate, downloadUpdate, switchVersion, updateInfo } = useUpdate();

  const onPress = async () => {
    await checkUpdate();
  };

  if (updateInfo?.update) {
    return (
      <Button
        title={`Install ${updateInfo.name}`}
        onPress={async () => {
          if (await downloadUpdate()) {
            await switchVersion();
          }
        }}
      />
    );
  }
  return <Button title="Check for updates" onPress={onPress} />;
}

Rollback

CodePush rolled back an update when the app restarted without calling notifyAppReady(). Cresc does the same automatically: if a new version crashes before it is marked successful, the client reverts to the previous good version on the next launch. On top of that, a native check runs on every cold start without JavaScript, so a version that kills JS on launch can still be replaced with a fixed one. See Force Boot.

5. CLI mapping

App Center CLICresc CLI
appcenter logincresc login, or an API key in CI
appcenter apps createcresc createApp --platform <ios|android>
(no equivalent)cresc uploadIpa / cresc uploadApk / cresc uploadAab to register the store build
appcenter codepush release-reactcresc bundle --platform <ios|android>
--targetBinaryVersion "1.2.x"--packageVersion 1.2.0 or --packageVersionRange ">=1.2 <1.3"
--description "..."--description "..."
--rollout 20--rollout 20 (staged rollout by device bucket)
appcenter codepush patch --rollout 100cresc update --versionId <id> --rollout 100
appcenter codepush deployment historycresc versions, or the dashboard

The one new step is uploading the native build. Cresc diffs each update against the exact package users installed, which is how a one-line fix ships as a 3.4 KB patch instead of a full bundle. Upload it once per native release: Publishing.

A CI job that used to run appcenter codepush release-react -a org/app-ios -d Production becomes:

cresc bundle --platform ios --name "$GIT_TAG" --description "$CHANGELOG" \
  --packageVersion "$NATIVE_VERSION" --rollout 20 --no-interactive

Full option list: CLI Tools.

6. Staging and Production deployments

CodePush separated testers from users with Staging and Production deployment keys. Cresc uses one appKey per platform and gives you two ways to stage a release instead:

  • Staged rollout: publish with --rollout 5, watch adoption and errors in Analytics and JS Error Monitoring, then raise the percentage. Moving to 100% promotes the version to a full release.
  • Test QR codes: testers scan a version's QR code from the dashboard and load it without binding it to any native package, either through a deep link with no code changes or through parseTestQrCode in your own scanner. See Best Practices.

FAQ

Do I have to rewrite my app to leave CodePush?

No. You swap the native bundle URL, replace the codePush() wrapper with <UpdateProvider>, and port any manual checkForUpdate code using the tables above. Components and business logic stay the same.

Can users on a CodePush build receive Cresc updates?

Not until they install a store build that includes react-native-update. Ship one native release with Cresc integrated, upload that build to Cresc, and publish OTA updates against it from then on.

What replaces codePush.notifyAppReady()?

Nothing, by default. Cresc marks a version successful automatically after it starts. Set autoMarkSuccess: false and call markSuccess() yourself only if you want to confirm a version later, for example after a critical screen has rendered.

Is there a self-hosted option like code-push-server?

Yes. The client SDK, CLI and admin UI are open source. See Expo Updates vs Cresc for hosting tradeoffs, or pricing for the hosted plans, which include CDN delivery.