HotupdaterHot Updater
Get Started

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-dev

Step 2: Initialize Your Provider

Run the initialization command to set up your provider interactively:

npx hot-updater init

This interactive CLI will guide you through:

  1. Build Plugin Selection: Choose your build system (Bare/React Native CLI, Re.Pack, or Expo)

  2. Provider Selection: Select from available providers:

  3. 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-name

hot-updater.config.ts - Contains provider integration settings:

hot-updater.config.ts
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:

App.tsx
import { HotUpdater, getUpdateSource } from "@hot-updater/react-native";

function App() {
  return (
    <View>
      <Text>Hello World</Text>
    </View>
  );
}

export default HotUpdater.wrap({
  source: getUpdateSource("https://your-update-server-url/api/check-update", { 
    updateStrategy: "appVersion", // or "fingerprint"
  }), 
  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.

Plugin Configuration

Configure the build plugin to set the bundle ID at build time. This is necessary for integrating the hot-updater plugin into your project.

Add the following to your babel.config.js file:

babel.config.js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    'hot-updater/babel-plugin', 
  ],
};

First, install the Re.Pack plugin:

npm install @hot-updater/repack --save-dev

Then add the following to your rspack.config.mjs file:

rspack.config.mjs
import { HotUpdaterPlugin } from "@hot-updater/repack";

export default {
  // ...
  plugins: [
    new Repack.RepackPlugin(),
    new HotUpdaterPlugin() 
  ],
};

Add the plugin to your app.json file as shown below:

app.json
{
  "expo": {
    "plugins": [
      [
        "@hot-updater/react-native",
        {
          "channel": "production"
        }
      ]
    ]
  }
}

Run prebuild to apply the changes:

npx expo prebuild

Run the following command to customize your Babel configuration:

npx expo customize babel.config.js

Add the following to your babel.config.js file:

babel.config.js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    'hot-updater/babel-plugin', 
  ],
};

For Expo projects, the plugin automatically generates the native code configuration described in the next section.

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:

android/app/src/main/java/com/<your-app-name>/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:

android/app/src/main/java/com/<your-app-name>/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:

android/app/src/main/java/com/<your-app-name>/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:

ios/<your-app-name>/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:

ios/<your-app-name>/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
}

@end

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 deploy

Use the interactive mode for guided deployment:

npx hot-updater deploy -i

For emergency updates with forced reload:

npx hot-updater deploy -i -f

Monitor and Manage Updates

Open the console to track deployments, manage versions, and perform rollbacks:

npx hot-updater console

Explore Advanced Features

Learn more about advanced capabilities: