Skip to main content

Choicely RN — Android Integration Guide

Integrate the Choicely React Native SDK into your native Android project.

Versions

ArtifactVersion
com.choicely.sdk:android-core1.1.2
com.choicely.sdk:android-rn0.0.2
com.choicely.sdk:choicely-rn-gradle0.0.4

Prerequisites

  • Node.js >= 20
  • Android SDK with CMake
  • Git
  • Java 17

Project Structure (after setup)

Android/Java/
├── app/                        ← your Android app module
├── app-react-native/           ← cloned choicely-rn repo
│   ├── node_modules/
│   ├── package.json
│   └── react-native.config.js
├── build.gradle
├── settings.gradle
├── gradle.properties
└── gradle/libs.versions.toml

Setup

Step 1 — Clone the Choicely RN repo

cd Android/Java
git clone https://github.com/choicely/choicely-rn.git app-react-native
cd app-react-native && npm install && cd ..
The folder must be named app-react-native by default. To use a different name, set choicelyRnDir in gradle.properties (see Step 2).

Step 2 — gradle.properties

newArchEnabled=true
hermesEnabled=true

# RN module folder name — change this ONE line if you rename the folder
choicelyRnDir=app-react-native

Step 3 — settings.gradle

pluginManagement {
    ......
    // Load the RN Gradle plugin from your local node_modules
    includeBuild("${settings.providers.gradleProperty('choicelyRnDir').orElse('app-react-native').get()}/node_modules/@react-native/gradle-plugin")
}
plugins {
    id "com.facebook.react.settings"
}
def rnDir = settings.providers.gradleProperty("choicelyRnDir").orElse("app-react-native").get()
reactSettings {
    autolinkLibrariesFromCommand(
        [ ["/opt/homebrew/bin/node", "/usr/local/bin/node"].find { new File(it).exists() } ?: "node",
          "node_modules/react-native/cli.js", "config" ],
        file(rnDir)
    )
}
Why includeBuild and reactSettings can’t be automated by a plugin: They run in Gradle’s settings phase — before any plugin from Maven Central can load. These will always need to be in your settings.gradle.

Step 4 — Root build.gradle

buildscript {
    ext.kotlin_version = '2.2.20'
    ext {
        minSdkVersion      = 26
        compileSdkVersion   = 36
        buildToolsVersion   = "35.0.0"
        targetSdkVersion    = 36
        reactNativeVersion  = "0.82.0"
        REACT_NATIVE_NODE_MODULES_DIR = file("${rootDir}/${choicelyRnDir}/node_modules/react-native").canonicalPath
        REACT_NATIVE_WORKLETS_NODE_MODULES_DIR = file("${rootDir}/${choicelyRnDir}/node_modules/react-native-worklets").canonicalPath
    }
    repositories {
        google()
        mavenLocal()
        mavenCentral()
        maven { url "$rootDir/${choicelyRnDir}/node_modules/react-native/android" }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.13.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // Choicely RN Gradle plugin — provides com.choicely.rn.setup & com.choicely.react
        classpath 'com.choicely.sdk:choicely-rn-gradle:0.0.4'
    }
}

plugins {
    alias(libs.plugins.android.application) apply false
    id "com.facebook.react" apply false
}

// REQUIRED: must be applied on the root project
apply plugin: 'com.choicely.rn.setup'

Step 5 — App build.gradle

plugins {
    alias(libs.plugins.android.application)
}
// com.facebook.react must be applied directly (Gradle ClassLoaderScope limitation)
apply plugin: 'com.choicely.react'
apply plugin: 'com.facebook.react'

// Vector icons
def rnDir = findProperty("choicelyRnDir") ?: "app-react-native"
project.ext.vectoricons = [iconFontsDir: "${rootDir}/${rnDir}/node_modules/react-native-vector-icons/Fonts"]
apply from: "${rootDir}/${rnDir}/node_modules/react-native-vector-icons/fonts.gradle"

android {
    ......
    namespace 'com.your.app'

    // Required: resolve duplicate libworklets.so from reanimated & worklets
    packagingOptions {
        pickFirst 'lib/arm64-v8a/libworklets.so'
        pickFirst 'lib/x86/libworklets.so'
        pickFirst 'lib/x86_64/libworklets.so'
        pickFirst 'lib/armeabi-v7a/libworklets.so'
    }
}
dependencies {
    // Choicely SDK
    implementation("com.choicely.sdk:android-core:1.1.2")
    implementation("com.choicely.sdk:android-rn:0.0.2")

}

Also needed — react-native.config.js

File: app-react-native/react-native.config.js
Must use dots in the filename — NOT react-native-config.js.
module.exports = {
  project: {
    android: {
      sourceDir: "../",
      appName: "app",
      packageName: "com.your.app", // must match applicationId
    },
  },
};

Step 6 — MyApplication.java

Extend ChoicelyRNApplication instead of Application and initialise both the RN engine and the Choicely SDK.
package com.your.app;

import com.choicely.rn.ChoicelyRNApplication;
import com.choicely.rn.ChoicelyRNHost;
import com.choicely.rn.utils.ChoicelyRNConfig;
import com.choicely.sdk.ChoicelySDK;
import com.facebook.react.PackageList;
import com.facebook.react.ReactNativeApplicationEntryPoint;

public class MyApplication extends ChoicelyRNApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        String appKey = "YOUR_APP_KEY";
        initReactNative(appKey);
        initChoicely(appKey);

        // Bundle loading:
        //   false = load LOCAL bundle (use this during development)
        //   true  = load REMOTE bundle from server
        // For local: run `npm run bundle` in app-react-native/ first to generate the bundle file.
        ChoicelyRNConfig.serLoadRemoteBundle(getApplicationContext(), false, appKey);
    }

    @Override
    protected void initReactNative(String appKey) {
        ChoicelyRNHost rnHost = new ChoicelyRNHost(this, appKey) {};
        initRNEngine(rnHost);
        ReactNativeApplicationEntryPoint.loadReactNative(this);
        addChoicelyBridge(new PackageList(this).getPackages());
    }

    private void initChoicely(String appKey) {
        ChoicelySDK.init(this, appKey);
        // Load the latest bundle from remote (non-blocking, updates on next launch)
        ChoicelyRNConfig.loadUpdatedBundle(appKey, getApplicationContext(), null);
    }
}

Bundle loading explained

serLoadRemoteBundle second argBehaviour
falseLoads the local JS bundle packaged in your APK. Run npm run bundle inside app-react-native/ to rebuild it after JS changes.
trueLoads the remote bundle from the Choicely server at runtime. Use this in production.

Artifacts Overview

Gradle Plugin: com.choicely.sdk:choicely-rn-gradle

Distributed via Maven Central Snapshots. Provides two plugin IDs:
Plugin IDApplied inPurpose
com.choicely.rn.setupRoot build.gradleNode binary detection, lib patching, CMake fix, PATH injection
com.choicely.reactApp build.gradleConfigures react{} paths, ext vars, autolinkLibrariesWithApp()

Runtime Library: com.choicely.sdk:android-rn

AAR library containing:
ComponentDescription
ChoicelyRNApplicationAbstract Application implementing ReactApplication, manages multiple ReactHost instances
ChoicelyRNHostExtends DefaultReactNativeHost, handles JS bundle loading (asset or remote)
ChoicelyDefaultReactHostFactory for creating ReactHost instances
ChoicelyReactNativeFragmentFragment for embedding RN views in native screens
RNFragmentWrapperWrapper around the RN fragment
ChoicelyDeepLinkScreenActivityActivity for deep link handling
ChoicelyBridgePackage / ChoicelyRNBridgeNative-to-RN bridge module
ChoicelyRNConfigConfig for current version/app key for remote bundles
ChoicelyRemoteBundleHandles remote JS bundle downloading
Transitive dependencies (pulled automatically):
  • com.choicely.sdk:android-core
  • com.facebook.react:react-android:0.82.0
  • com.facebook.react:hermes-android:0.82.0
  • androidx.databinding:*:8.13.0
  • org.jetbrains.kotlin:kotlin-stdlib:2.1.21

Build Commands

# Build JS bundle locally (required when serLoadRemoteBundle = false)
cd app-react-native && npm run bundle && cd ..

# Build APK
./gradlew :app:assembleDebug

# Clean + Build
./gradlew clean :app:assembleDebug

Renaming the RN folder

Change one line in gradle.properties:
choicelyRnDir=my-custom-folder-name

What the Gradle plugin handles automatically

WhatPlugin ID
Node binary detection (macOS Homebrew / /usr/local)com.choicely.rn.setup
Patching third-party libs that hardcode "node"com.choicely.rn.setup
Injecting Node into PATH for all Exec taskscom.choicely.rn.setup
CMake cache clean fixcom.choicely.rn.setup
react{} block (node, cliFile, reactNativeDir, codegenDir)com.choicely.react
autolinkLibrariesWithApp()com.choicely.react

What still requires manual setup (Gradle hard limits)

WhatWhy
includeBuild(".../gradle-plugin") in settings.gradleSettings phase — no plugin can run before this
reactSettings { autolinkLibrariesFromCommand(...) }Settings phase — same reason
apply plugin: 'com.facebook.react' in app/build.gradleClassLoaderScope — can’t be applied from another plugin

Troubleshooting

ErrorFix
Could not find project.android.packageNameRename to react-native.config.js (dots, not dashes)
includeBuild path not foundRun npm install in app-react-native/ first
pluginManagement must appear firstNo code before pluginManagement {} in settings.gradle
Node binary not foundPlugin finds Node automatically; ensure Homebrew or nvm is installed
Cannot find module '@react-native/codegen'choicelyRnDir may point to wrong folder — check gradle.properties
Failed to apply plugin 'com.choicely.react'Add apply plugin: 'com.choicely.rn.setup' in root build.gradle
2 files found with path 'lib/arm64-v8a/libworklets.so'Add packagingOptions { pickFirst 'lib/*/libworklets.so' } in app build.gradle
Included build 'choicely-rn-android/rn-gradle-plugin' does not existRemove the includeBuild("choicely-rn-android/rn-gradle-plugin") line — the plugin is now distributed via Maven