> ## Documentation Index
> Fetch the complete documentation index at: https://docs.choicely.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native Support

> Integrate a React Native module into an existing native Android project

# Choicely RN — Android Integration Guide

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

<Note>
  This guide targets the published SDK artifacts listed below and is verified against
  them. It is for teams embedding Choicely into an existing native Android app. If you
  want to build and publish an app without writing code, use
  [Choicely Studio](https://www.choicely.com) instead.
</Note>

***

## Versions

| Artifact | Version |
| - | - |
| `com.choicely.sdk:android-core` | `1.1.2` |
| `com.choicely.sdk:android-rn` | `0.0.2` |
| `com.choicely.sdk:choicely-rn-gradle` | `0.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 rn-starter 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

```bash theme={null}
cd Android/Java
git clone https://github.com/choicely/rn-starter.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`

```properties theme={null}
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`

```groovy theme={null}
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`

```groovy theme={null}
buildscript {
    ext.kotlin_version = '2.2.20'
    ext {
        minSdkVersion      = 26
        compileSdkVersion   = 36
        buildToolsVersion   = "35.0.0"
        ndkVersion          = "27.1.12297006"
        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
        // Required: notifee, netinfo, blur, geolocation and pager-view locate the React
        // Native android prebuilts by walking UP from rootDir, which fails when
        // node_modules lives inside the RN module folder. These are their escape hatches.
        reactNativeAndroidRoot = file("${rootDir}/${choicelyRnDir}/node_modules/react-native")
        set('react-native', [options: [reactNativeAndroidDir: "../../../react-native"]])
    }
    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`

```groovy theme={null}
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'
    ndkVersion rootProject.ext.ndkVersion

    // 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`.

```js theme={null}
module.exports = {
  project: {
    android: {
      sourceDir: "../",
      appName: "app",
      packageName: "com.your.app", // must match applicationId
    },
  },
};
```

***

### Step 5b — Required app colours

The Choicely SDK themes reference these. Without them the build fails at
`Android resource linking failed … resource color/colorPrimaryDark not found`.

**File:** `app/src/main/res/values/colors.xml`

```xml theme={null}
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#FF6200EE</color>
    <color name="colorPrimaryDark">#FF3700B3</color>
    <color name="colorAccent">#FF03DAC5</color>
</resources>
```

***

### Step 6 — `MyApplication.java`

Extend `ChoicelyRNApplication` instead of `Application` and initialise both the RN engine and the Choicely SDK.

```java theme={null}
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 arg | Behaviour |
| - | - |
| `false` | Loads the **local** JS bundle packaged in your APK. Run `npm run bundle` inside `app-react-native/` to rebuild it after JS changes — it writes `app/src/main/assets/index.android.bundle`, which is where the SDK looks. Debug variants do **not** bundle automatically, so this step is required before the first debug build. |
| `true` | Loads 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 ID | Applied in | Purpose |
| - | - | - |
| `com.choicely.rn.setup` | Root `build.gradle` | Node binary detection, lib patching, CMake fix, PATH injection |
| `com.choicely.react` | App `build.gradle` | Configures `react{}` paths, ext vars, `autolinkLibrariesWithApp()` |

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

AAR library containing:

| Component | Description |
| - | - |
| `ChoicelyRNApplication` | Abstract `Application` implementing `ReactApplication`, manages multiple `ReactHost` instances |
| `ChoicelyRNHost` | Extends `DefaultReactNativeHost`, handles JS bundle loading (asset or remote) |
| `ChoicelyDefaultReactHost` | Factory for creating `ReactHost` instances |
| `ChoicelyReactNativeFragment` | Fragment for embedding RN views in native screens |
| `RNFragmentWrapper` | Wrapper around the RN fragment |
| `ChoicelyDeepLinkScreenActivity` | Activity for deep link handling |
| `ChoicelyBridgePackage` / `ChoicelyRNBridge` | Native-to-RN bridge module |
| `ChoicelyRNConfig` | Config for current version/app key for remote bundles |
| `ChoicelyRemoteBundle` | Handles 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

```bash theme={null}
# Build JS bundle locally (required when serLoadRemoteBundle = false)
# Writes app/src/main/assets/index.android.bundle
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`:

```properties theme={null}
choicelyRnDir=my-custom-folder-name
```

***

## What the Gradle plugin handles automatically

| What | Plugin 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 tasks | `com.choicely.rn.setup` |
| CMake cache clean fix | `com.choicely.rn.setup` |
| `react{}` block (node, cliFile, reactNativeDir, codegenDir) | `com.choicely.react` |
| `autolinkLibrariesWithApp()` | `com.choicely.react` |

## What still requires manual setup (Gradle hard limits)

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

***

## Troubleshooting

| Error | Fix |
| - | - |
| `Could not find project.android.packageName` | Rename to `react-native.config.js` (dots, not dashes) |
| `includeBuild path not found` | Run `npm install` in `app-react-native/` first |
| `pluginManagement must appear first` | No code before `pluginManagement {}` in `settings.gradle` |
| `Node binary not found` | Plugin 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 exist` | Remove the `includeBuild("choicely-rn-android/rn-gradle-plugin")` line — the plugin is now distributed via Maven |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.