Basic Usage
This guide walks you through the essential steps to integrate hot-updater into your React Native project.
Provider Setup
Before configuring the build plugin, you need to set up a provider to handle updates. Hot-updater supports multiple managed providers that handle storage and update distribution.
Step 1: Install hot-updater
First, install the hot-updater package:
npm install hot-updater --save-devStep 2: Initialize Your Provider
Run the initialization command to set up your provider interactively:
npx hot-updater initThis interactive CLI will guide you through:
-
Build Plugin Selection: Choose your build system (Bare/React Native CLI, Rock, or Expo)
-
Provider Selection: Select from available providers:
-
Provider Configuration: Follow provider-specific prompts for authentication and resource setup.
Step 3: Generated Configuration
After initialization, two files will be generated:
.env.hotupdater - Contains provider credentials and configuration:
# Example for Supabase
HOT_UPDATER_SUPABASE_URL=https://your-project.supabase.co
HOT_UPDATER_SUPABASE_ANON_KEY=your-anon-key
HOT_UPDATER_SUPABASE_BUCKET_NAME=your-bucket-namehot-updater.config.ts - Contains provider integration settings:
The example below uses the Bare (React Native CLI) build plugin. If you selected Rock or Expo, only the build field changes as shown in the build configuration section below.
import { bare } from "@hot-updater/bare";
import { supabaseStorage, supabaseDatabase } from "@hot-updater/supabase";
import { defineConfig } from "hot-updater";
import { config } from "dotenv";
config({ path: ".env.hotupdater" });
export default defineConfig({
build: bare({ enableHermes: true }),
storage: supabaseStorage({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseAnonKey: process.env.HOT_UPDATER_SUPABASE_ANON_KEY!,
bucketName: process.env.HOT_UPDATER_SUPABASE_BUCKET_NAME!,
}),
database: supabaseDatabase({
supabaseUrl: process.env.HOT_UPDATER_SUPABASE_URL!,
supabaseAnonKey: process.env.HOT_UPDATER_SUPABASE_ANON_KEY!,
}),
});For detailed provider-specific setup instructions, see the Self-Hosting (Managed) documentation.
The environment variables in .env are used only during deployment, not in your app bundle. However, if you're using react-native-dotenv, review the Security guidelines.
Step 4: Wrap Your Application
Add the HotUpdater wrapper to your main application component:
import { HotUpdater } from "@hot-updater/react-native";
function App() {
return (
<View>
<Text>Hello World</Text>
</View>
);
}
export default HotUpdater.wrap({
baseURL: "https://your-update-server-url/api/check-update",
updateStrategy: "appVersion", // or "fingerprint"
updateMode: "auto",
requestHeaders: {
// Optional: Add custom headers if needed
},
fallbackComponent: ({ progress, status }) => (
<View
style={{
flex: 1,
padding: 20,
borderRadius: 10,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
}}
>
<Text style={{ color: "white", fontSize: 20, fontWeight: "bold" }}>
{status === "UPDATING" ? "Updating..." : "Checking for Update..."}
</Text>
{progress > 0 ? (
<Text style={{ color: "white", fontSize: 20, fontWeight: "bold" }}>
{Math.round(progress * 100)}%
</Text>
) : null}
</View>
),
})(App);The update server URL will be provided after running npx hot-updater init. Check your terminal output or provider console for the correct URL.
Build Configuration
Hot Updater now configures bundling through the build field in hot-updater.config.ts. Legacy build-time plugin examples are no longer used.
Use the Bare build plugin for standard React Native CLI projects:
import { bare } from "@hot-updater/bare";
import { defineConfig } from "hot-updater";
export default defineConfig({
build: bare({
enableHermes: true,
}),
// ... storage and database
});No additional Babel plugin setup is required.
Use the Rock build plugin for Rock projects:
import { rock } from "@hot-updater/rock";
import { defineConfig } from "hot-updater";
export default defineConfig({
build: rock(),
// ... storage and database
});No additional Babel plugin setup is required.
Use the Expo build plugin in hot-updater.config.ts:
import { expo } from "@hot-updater/expo";
import { defineConfig } from "hot-updater";
export default defineConfig({
build: expo(),
// ... storage and database
});Then add the config plugin to your app.json:
{
"expo": {
"plugins": [
[
"@hot-updater/react-native",
{
"channel": "production"
}
]
]
}
}Run prebuild to apply the native changes:
npx expo prebuildFor Expo projects, the config plugin automatically generates the native code configuration described in the next section.
If you use Expo DOM components (use dom), see Expo DOM Components (use dom) for the additional Babel plugin setup.
Native Code Setup
To complete the integration of hot-updater, you'll need to add native code modifications for both Android and iOS platforms. This step ensures the hot-updater can interact with your app's underlying framework to apply updates seamlessly.
If you're using Expo with the plugin configured above, this step is handled automatically during prebuild.
Android
For React Native 0.82 and above, modify your MainApplication.kt:
package com.hotupdaterexample
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.hotupdater.HotUpdater
class MainApplication : Application(), ReactApplication {
override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
},
jsBundleFilePath = HotUpdater.getJSBundleFile(applicationContext),
)
}
override fun onCreate() {
super.onCreate()
loadReactNative(this)
}
}For React Native versions below 0.82, modify your MainApplication.kt:
package com.hotupdaterexample
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import com.hotupdater.HotUpdater
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
override fun getJSBundleFile(): String? {
return HotUpdater.getJSBundleFile(applicationContext)
}
}
override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
}
}For Java-based projects, modify your MainApplication.java:
package com.hotupdaterexample;
import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import java.util.List;
import com.hotupdater.HotUpdater;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new DefaultReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected String getJSBundleFile() {
return HotUpdater.Companion.getJSBundleFile(this.getApplication().getApplicationContext());
}
@Override
protected boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
}
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
}iOS
For Swift projects, modify your AppDelegate.swift:
import UIKit
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
import HotUpdater
@main
class AppDelegate: RCTAppDelegate {
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
self.moduleName = "HotUpdaterExample"
self.dependencyProvider = RCTAppDependencyProvider()
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = [:]
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func sourceURL(for bridge: RCTBridge) -> URL? {
self.bundleURL()
}
override func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
HotUpdater.bundleURL()
#endif
}
}For Objective-C projects, modify your AppDelegate.mm:
#import "AppDelegate.h"
#import <HotUpdater/HotUpdater.h>
#import <React/RCTBundleURLProvider.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"HotUpdaterExample";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [self bundleURL];
}
- (NSURL *)bundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
return [HotUpdater bundleURL];
#endif
}
@endFor brownfield apps where the JS bundle is embedded in an XCFramework, you can pass a custom Bundle instance to lookup the JavaScript bundle resource: HotUpdater.bundleURL(bundle: xcframeworkBundle) in Swift or [HotUpdater bundleURLWithBundle:xcframeworkBundle] in Objective-C.
Next Steps
Now that you've completed the setup, you can start deploying updates:
Deploy Your First Update
Build and publish an update to your users:
npx hot-updater deployUse the interactive mode for guided deployment:
npx hot-updater deploy -iFor emergency updates with forced reload:
npx hot-updater deploy -i -fFor staged rollouts, set the initial audience percentage explicitly:
npx hot-updater deploy -p ios -r 25Monitor and Manage Updates
Open the console to track deployments, manage versions, and perform rollbacks:
npx hot-updater consoleExplore Advanced Features
Learn more about advanced capabilities:
- Update Strategies - Choose between app version and fingerprint strategies
- Default Channel Setup - Configure the app's default release channel
- Runtime Channel Switch - Temporarily move a device to another channel without rebuilding
- Simulator Testing - Test updates in release mode before production
- Security Best Practices - Secure your update infrastructure