Install and connect native
Follow the path for React Native, Expo or HarmonyOS and prepare a Release build that can load updates.
Updated 2026-09-15
On this page
- Prepare your environment
- Install and obtain an appKey
- Which identifier goes where?
- Android: connect bundle loading
- Java projects
- iOS: use Pakta for Release bundles
- Swift and older AppDelegate templates
- Expo: build with native modules
- HarmonyOS: wire the RNOH integration
- HarmonyOS file changes
- Self-hosting: configure SDK and CLI separately
- Manual linking: only when autolinking is unavailable
- Android activity restoration with react-native-screens
- Optional: open a test update with a link
- Android AAB installers
- Next step
Prepare your environment
A React Native, Expo or RNOH project that already builds. Commands run from the app root containing
package.jsonunless stated otherwise.Node.js ≥18.17 and your existing platform toolchain; iOS builds require macOS.
A separate test application and device. Start with one platform.
SDK peer dependencies declare React ≥16.8 and RN ≥0.59; that is not a test guarantee for every version and architecture. Keep your project's version and follow its host structure below.
Install and obtain an appKey
Choose your project type. Run its commands in the directory containing package.json.
npm install -g rn-update-cli
npm install rn-updatenpm install -g rn-update-cli
npx expo install rn-updateFor iOS, run pod install inside ios (or bundle exec pod install with Bundler), then return to the app root. Expo generates its native project during the build step.
Then sign in and select an application:
pakta login
pakta createApp --platform android --name PaktaDemo
pakta selectApp --platform androidSkip createApp if the test application exists. For iOS / HarmonyOS use ios / harmony. Selection creates or updates update.json in the app root.
Which identifier goes where?
| Identifier | Source | Used by |
|---|---|---|
appKey | Application details or update.json | SDK new Pakta({ appKey }) |
appId | Application details or update.json | CLI / management API selection |
PAKTA_API_TOKEN | Console API token entry | CLI / CI only, never the app |
Follow only the section matching your project.
Android: connect bundle loading
If MainApplication.kt uses reactHost and getDefaultReactHost, add jsBundleFilePath to the existing host:
import cn.reactnative.modules.update.UpdateContext
// Keep existing ReactHost, PackageList and getDefaultReactHost imports.
override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList = PackageList(this).packages,
jsBundleFilePath = UpdateContext.getBundleUrl(this),
)
}For DefaultReactNativeHost / ReactNativeHost, import UpdateContext and override the method in the existing host object:
override fun getJSBundleFile(): String? =
UpdateContext.getBundleUrl(this@MainApplication)Disable PNG crunching in the existing release configuration to reduce resource byte differences:
android {
buildTypes {
release {
crunchPngs false
}
}
}Autolinking connects the module and the SDK's Gradle integration provides build identity. Do not invent buildTime. Retain existing signing and build settings.
Connect the root component, then run ./gradlew assembleRelease from android, or .\gradlew.bat assembleRelease on Windows. Use the matching Release task for flavored projects.
Java projects
Open android/app/src/main/java/your/package/MainApplication.java. Add import cn.reactnative.modules.update.UpdateContext; at the top and this override inside the existing ReactNativeHost or DefaultReactNativeHost object:
@Override
protected String getJSBundleFile() {
return UpdateContext.getBundleUrl(MainApplication.this);
}iOS: use Pakta for Release bundles
Install Pods from ios: bundle exec pod install for Bundler projects, otherwise pod install.
#import "RCTPakta.h"
- (NSURL *)bundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [RCTPakta bundleURL];
#endif
}Keep existing Debug entry points, module names and lifecycle methods. In Swift templates, the actual Release bundle URL provider must return RCTPakta.bundleURL(). Expose RCTPakta.h through the project's Objective-C bridging header instead of replacing the entire AppDelegate.
After connecting the root, open .xcworkspace in Xcode, select signing and a device, and verify a Release build. Retain the IPA from the same archive for registration.
Swift and older AppDelegate templates
Add #import "RCTPakta.h" to your app target's Objective-C bridging header. If needed, create that header and set its relative path, such as YourApp/YourApp-Bridging-Header.h, in Build Settings → Objective-C Bridging Header.
Modify the existing URL provider. Newer templates may put this method inside ReactNativeDelegate:
override func bundleURL() -> URL? {
#if DEBUG
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
return RCTPakta.bundleURL()
#endif
}For an older Objective-C template with only sourceURLForBridge:, retain that signature and return [RCTPakta bundleURL] in its non-DEBUG branch. Mixed native apps should obtain the URL through a bridge delegate rather than create the root view using a fixed bundle path.
Expo: build with native modules
Expo Go does not contain rn-update's native module. Follow these steps:
Install dependencies with
npx expo install rn-update. For a custom channel, add the built-in Config Plugin toexpo.plugins.Connect the root layout: wrap the existing
Stack/Slotinapp/_layout.tsxwithUpdateProvider; create the client outside components.Build the native app: run
npx expo run:android --variant release,npx expo run:ios --configuration Release, or your existing EAS production profile.Install and verify it starts without Metro. Later, create OTA packages using
bundle --expowith platformandroid/ios.
See the Expo guide for the complete steps.
HarmonyOS: wire the RNOH integration
HarmonyOS requires HAR, ArkTS and C++ integration beyond npm installation. Compare these locations with the example:
| Location | Connection |
|---|---|
harmony/entry/oh-package.json5 | Pakta HAR and RNOH dependencies, using your paths |
harmony/entry/hvigorfile.ts | Import SDK harmony/hvigor-plugin; add reactNativeUpdatePlugin() to plugins |
RNPackagesFactory.ets | Import PaktaPackage and add it to the RNPackage list |
PackageProvider.cpp / CMake | Register the Pakta C++ package and native linkage |
pages/Index.ets | Add PaktaFileJSBundleProvider, retaining embedded fallback |
pakta bundle --platform harmony --output .pakta/output/harmony.ppk --no-interactiveSync, sign and build the native .app in DevEco Studio. Confirm generated rawfile paths match the host, install and verify, then register with uploadApp.
Complete projects are in the SDK examples: testHotUpdate, expoUsePakta and harmony_use_pakta. Replace repository-relative paths with your dependency paths.
HarmonyOS file changes
These paths assume harmony and node_modules are siblings in the app root. Merge entries into existing dependencies, plugins and package lists. Retain all existing registrations. Add the HAR, Hvigor plugin, ArkTS and C++ registrations, then set the Release bundle provider:
"pakta": "file:../../node_modules/rn-update/harmony/pakta.har"import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { reactNativeUpdatePlugin } from '../../node_modules/rn-update/harmony/hvigor-plugin';
export default {
system: hapTasks,
plugins: [reactNativeUpdatePlugin()],
};import type { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony';
import PaktaPackage from 'pakta';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [new PaktaPackage(ctx)];
}set(PAKTA_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules/rn-update/harmony/pakta/src/main/cpp")
target_include_directories(rnoh_app PRIVATE "${PAKTA_CPP_DIR}")
target_sources(rnoh_app PRIVATE "${PAKTA_CPP_DIR}/PaktaTurboModule.cpp")#include "RNOH/PackageProvider.h"
#include "PaktaPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {std::make_shared<PaktaPackage>(ctx)};
}import { PaktaFileJSBundleProvider } from 'pakta';
import { AnyJSBundleProvider, ResourceJSBundleProvider } from '@rnoh/react-native-openharmony';jsBundleProvider: new AnyJSBundleProvider([
new PaktaFileJSBundleProvider(this.rnohCoreContext.uiAbilityContext),
new ResourceJSBundleProvider(
this.rnohCoreContext.uiAbilityContext.resourceManager,
'bundle.harmony.js'
),
]),Keep the other RNApp properties and ensure rnohCoreContext is ready. Release builds should try the Pakta file before the embedded resource, with no Metro provider ahead of it.
See the complete Expo guide for its root layout and channel plugin.
Self-hosting: configure SDK and CLI separately
Hosted-service users can skip this section. The SDK uses the public update API; the CLI uses management endpoints:
server: {
main: ['https://YOUR_HOST/api'],
queryUrls: ['https://YOUR_CDN/endpoints.json'],
},Replace hosts with your deployment addresses. The discovery file must belong to your deployment. Set the CLI host before signing in:
$env:RNU_SERVICE_URL = 'https://YOUR_HOST'export RNU_SERVICE_URL=https://YOUR_HOSTManual linking: only when autolinking is unavailable
React Native 0.60+ normally autolinks. Run npx react-native config to check rn-update discovery and install iOS Pods. Skip manual registration if already linked.
For older Android projects add the library in settings.gradle, reference it in app dependencies, and add new UpdatePackage() to the existing getPackages list, importing cn.reactnative.modules.update.UpdatePackage. Configure all three:
include ':rn-update'
project(':rn-update').projectDir = new File(rootProject.projectDir, '../node_modules/rn-update/android')implementation project(':rn-update')If iOS discovery fails, add pod 'rn-update', :path => '../node_modules/rn-update' inside the app target in Podfile and run pod install. Linking alone does not configure the bundle loading entry shown above.
Android activity restoration with react-native-screens
For apps using react-native-screens, check MainActivity restoration configuration. The following requires a version that supplies RNScreensFragmentFactory. Place the override directly in MainActivity, not MainActivityDelegate:
import android.os.Bundle
import com.swmansion.rnscreens.fragment.restoration.RNScreensFragmentFactory
override fun onCreate(savedInstanceState: Bundle?) {
supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
super.onCreate(savedInstanceState)
}Merge into an existing onCreate instead of adding a duplicate. Older releases without this class need their own screens installation instructions.
Optional: open a test update with a link
An existing scanner can call parseTestQrCode. To open the app from a browser or camera, configure your own scheme such as paktademo.
Add a separate filter inside the existing Android MainActivity activity, keep its launcher filter, and use android:launchMode="singleTask":
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="paktademo" />
</intent-filter>For iOS add paktademo in the app target's Info → URL Types and retain the app's React Native Linking forwarding in AppDelegate. Expo uses expo.scheme. Rebuild the native installer.
After uploading a test update, copy its full hash and open paktademo://update?type=__rnPaktaVersionHash&data=ACTUAL_HASH. The Provider checks it and follows your app's download/activation flow. testChannel:false rejects test payloads.
Android AAB installers
Retain the AAB actually submitted to Google Play. Inspect with pakta parseAab PATH, then register with pakta uploadAab PATH. Do not substitute an APK from a different build.
Do not disable every language, density and ABI split just to integrate. Verify resources and updates using the distributed build. Investigate resource packaging and the native version when a split lacks an asset.
Next step
Dependencies are installed, the CLI has selected the correct platform application, and native loading is configured. Connect your app, add the version panel, then build the native package.
Local Expo build flags follow the official Expo CLI reference.
