PageSense Android SDK React Native JS Bridge Integration Guide

PageSense Android SDK React Native JS Bridge Integration Guide

What is the React Native Native Bridge

The React Native Native Bridge is the communication layer between the JavaScript runtime and the native platform code (Android or iOS).
It allows JavaScript code to:
  1. Invoke native functions
  2. Access platform-specific SDK features
  3. Receive responses from native modules
In the context of the PageSense Android SDK, the bridge enables the React Native application to call Android SDK methods such as:
  1. SDK initialization
  2. Experiment activation
  3. Variation retrieval
  4. Goal tracking
These native methods are implemented in Kotlin and exposed to React Native through the React Native bridge API.

Architecture of the Android SDK Integration with React Native Applications

React Native applications primarily execute JavaScript code, whereas the PageSense Android SDK is implemented using Kotlin/Java. Since JavaScript cannot directly interact with native Android code, React Native provides a mechanism known as the Native Bridge. The React Native Native Bridge enables communication between the JavaScript layer and native Android modules, allowing React Native applications to invoke Android SDK functionality.

PageSense Android SDK React Native – Native Bridge Architecture 

React Native UI                                                               
React Native Wrapper (TypeScript/JavaScript)            
React Native Native Bridge                                           
PageSenseSDKPackage (Module Registration)           
PageSenseSDKModule (Kotlin Native Module)          
PageSense Android SDK                                               

In this integration, a native module is implemented to expose the PageSense Android SDK methods to the React Native application. The native module internally interacts with the PageSense Android SDK to perform operations such as initialising the SDK, activating experiments, and tracking experiment goals.
Once registered with the React Native runtime, the native module becomes accessible to the JavaScript layer through React Native’s NativeModules API. A React Native wrapper can then invoke the exposed native functions, enabling the application to run experiments and track goals directly from the React Native codebase.

PageSense SDK Capabilities

The PageSense SDK exposes the following functions.

Feature

Function

Description

Initialize SDK

createNewPageSenseClient

Creates and initialises a PageSense client instance in the application. This client instance is responsible for communicating with the PageSense service and managing experiments for the user session.

Activate Experiment

activateExperiment

Activates a specific experiment for the user and allocates them to an appropriate variation based on the experiment configuration.

Get Variation Name

getVariationName

Retrieves the name of the variation assigned to the current user for a particular experiment, allowing the application to apply the corresponding UI or behaviour changes.

Track Goal

trackGoal

Records and tracks conversion events or goals defined in the experiment, helping measure the effectiveness and performance of different variations.

Integration Prerequisites

Before integrating the PageSense Android SDK with a React Native application, ensure the following tools are installed.

Development Environment
  1. Node.js
  2. npm
  3. Watchman
  4. React Native CLI
Android Development Tools
  1. Android Studio
  2. Android SDK
  3. Gradle
  4. Java Development Kit (JDK)

Integration Prerequisites

Before integrating the PageSense Android SDK with a React Native application, ensure the following tools are installed.
Development Environment
Node.js
npm
Watchman
React Native CLI

Android Development Tools
Android Studio
Android SDK
Gradle
Java Development Kit (JDK)

Verify installation:
# Check the installed Node.js version
node -v
# Verify the installed npm version
npm -v
# Verify the Android environment
npx react-native doctor


Installing the PageSense Android SDK in the React Native Application

The PageSense Android SDK is distributed through the Zoho Maven Repository. To integrate the SDK into a React Native Android application, the repository must be configured and the SDK dependency must be added to the project.

Step 1 – Add the Zoho Maven Repository
Add the PageSense Maven repository so that Gradle can download the SDK artifacts. In modern React Native projects (Gradle 7+), repositories are typically configured in settings.gradle.

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url "https://maven.zohodl.com/"
        }
    }
}

In some project configurations, the repository may also be added in the project-level build.gradle file located in the Android project root. Adding the repository allows Gradle to resolve and download the PageSense Android SDK dependency.

repositories {
    google()
    mavenCentral()
    maven {
        url "https://maven.zohodl.com/"
    }
}

Step 2 – Add the PageSense Android SDK Dependency

Add the PageSense Android SDK dependency to the application-level Gradle file located at: android/app/build.gradle
Add the dependency inside the dependencies block:

dependencies {
    implementation("com.facebook.react:react-android")
    implementation("com.zoho.pagesense:pagesense:1.1.2")
}

Once added, Gradle will automatically download and include the PageSense Android SDK in the application during the build process.

Android React Native Libraries

Ensure that the required React Native libraries are available in the Android project. These libraries are automatically included as part of the React Native Android build configuration and are installed when the project dependencies are resolved using Gradle. The following React Native libraries must be available in the project as part of the Android build setup:
  1. react-android – Core React Native runtime library for Android that enables the JavaScript execution environment and communication with native modules.
  2. hermes-android (optional) – JavaScript engine used by React Native applications for improved performance and reduced startup time.
These dependencies are typically included in the application-level Gradle file located at android/app/build.gradle. In addition, ensure that the following React Native Android bridge libraries are available for implementing native modules:
  1. ReactContextBaseJavaModule
  2. ReactMethod
  3. PromiseReadableMap
  4. ReactPackage
  5. ReactApplicationContext
Android React Native Libraries
Ensure that the required React Native libraries are available in the Android project. These libraries are automatically included as part of the React Native Android build configuration and are installed when the project dependencies are resolved using Gradle. The following React Native libraries must be available in the project as part of the Android build setup:
react-android – Core React Native runtime library for Android that enables the JavaScript execution environment and communication with native modules.
hermes-android (optional) – JavaScript engine used by React Native applications for improved performance and reduced startup time.
These dependencies are typically included in the application-level Gradle file located at android/app/build.gradle. In addition, ensure that the following React Native Android bridge libraries are available for implementing native modules:
  1. ReactContextBaseJavaModule
  2. ReactMethod
  3. Promise
  4. ReadableMap
  5. ReactPackage
  6. ReactApplicationContext
These libraries enable the implementation of native modules in Kotlin or Java and allow the PageSense Android SDK to be exposed to the React Native application through the React Native Native Bridge.

Creating the Native Bridge Module

In Android, React Native native modules are implemented in three parts:
  1. Kotlin Native Module Implementation
  2. React Native Package Implementation
  3. React Native Package Registration
Android directly exposes native methods using the React Native bridge APIs.

i. Creating the Kotlin Native Module

Create the native module file for the PageSense Android SDK using Kotlin. This file should be placed within the Android application Java Source Package directory located inside the android folder of the React Native project’s root directory.

Example: /ProjectRoot/android/app/src/main/java/com/sampleapp/PageSenseSDKModule.kt
  1. package com.sampleapp
  2.  
  3. import com.facebook.react.bridge.*
  4. import com.facebook.react.module.annotations.ReactModule
  5. import com.zoho.pagesense.android.abtesting.PageSenseClient
  6. import com.zoho.pagesense.android.abtesting.PageSenseClientBuilder
  7. import com.zoho.pagesense.android.abtesting.PageSenseSDKOptions
  8. import com.zoho.pagesense.android.network.ProjectSettingsCallBack
  9. import com.zoho.pagesense.android.logging.LogLevel
  10.  
  11. /**
  12.  * React Native Native Module exposing PageSense Android SDK functionality to the React Native
  13.  * JavaScript layer through the React Native bridge.
  14.  */
  15. @ReactModule(name = PageSenseSDKModule.NAME)
  16. class PageSenseSDKModule(private val reactContext: ReactApplicationContext) :
  17.     ReactContextBaseJavaModule(reactContext) {
  18.  
  19.     companion object {
  20.         const val NAME = "PageSenseSDKModule"
  21.         private const val LOG_TAG = "[PageSenseSDK]"
  22.     }
  23.  
  24.     private var client: PageSenseClient? = null
  25.     private var isInitialized = false
  26.  
  27.     override fun getName(): String {
  28.         return NAME
  29.     }
  30.  
  31.     /**
  32.      * Initializes the PageSense SDK and creates the PageSenseClient instance.
  33.      * @param accountId PageSense Account ID associated with the project
  34.      * @param sdkKey SDK key used to authenticate the client
  35.      * @param projectName Name of the PageSense project
  36.      * @param promise React Native Promise used to return the initialization result
  37.      * Returns: true = SDK successfully initialized, false = SDK initialization failed
  38.      */
  39.     @ReactMethod
  40.     fun initialiseSDK(
  41.         accountId: String,
  42.         sdkKey: String,
  43.         projectName: String,
  44.         promise: Promise
  45.     ) {
  46.         if (isInitialized) {
  47.             println("$LOG_TAG SDK already initialized")
  48.             promise.resolve(true)
  49.             return
  50.         }
  51.  
  52.         println("$LOG_TAG Initializing PageSense SDK...")
  53.  
  54.         val pageSenseSDKOptions = PageSenseSDKOptions()
  55.         pageSenseSDKOptions.logLevel = LogLevel.DEBUG
  56.         pageSenseSDKOptions.pollingInterval = 5
  57.  
  58.         PageSenseClientBuilder.createNewPageSenseClient(
  59.             accountId,
  60.             sdkKey,
  61.             projectName,
  62.             pageSenseSDKOptions,
  63.             object : ProjectSettingsCallBack {
  64.                 override fun onFailure(message: String?, code: Int?) {
  65.                     println("[PageSense] SDK initialization failed: $message")
  66.                     promise.resolve(false)
  67.                 }
  68.                 override fun onSuccess(data: PageSenseClient?) {
  69.                     if (data != null) {
  70.                         client = data
  71.                         isInitialized = true
  72.                         println("[PageSense] SDK initialized successfully")
  73.                         promise.resolve(true)
  74.                     } else {
  75.                         println("[PageSense] SDK initialization returned null client")
  76.                         promise.resolve(false)
  77.                     }
  78.                 }
  79.             }
  80.         )
  81.     }
  82.  
  83.     /**
  84.      * Activates a PageSense FullStack A/B Testing experiment.
  85.      * @param experimentName Name of the experiment configured in PageSense
  86.      * @param userId Unique identifier representing the user
  87.      * @param attributes User attributes used for audience targeting
  88.      * @param promise React Native Promise used to return the variation
  89.      * Returns: variationName or null if SDK is not initialized or any error occurs
  90.      */
  91.     @ReactMethod
  92.     fun activateExperiment(
  93.         experimentName: String,
  94.         userId: String,
  95.         attributes: ReadableMap,
  96.         promise: Promise
  97.     ) {
  98.         val clientInstance = client
  99.         if (clientInstance == null) {
  100.             println("$LOG_TAG activateExperiment called before SDK initialization")
  101.             promise.resolve(null)
  102.             return
  103.         }
  104.         val attrs = readableMapToHashMap(attributes)
  105.         try {
  106.             val variationName = clientInstance.activateExperiment(experimentName, userId, attrs)
  107.             println("$LOG_TAG Variation activated: $variationName")
  108.             promise.resolve(variationName)
  109.         } catch (e: Exception) {
  110.             println("$LOG_TAG activateExperiment error: ${e.message}")
  111.             promise.resolve(null)
  112.         }
  113.     }
  114.  
  115.     /**
  116.      * Retrieves the variation assigned to a user without activating the experiment.
  117.      * @param experimentName Name of the experiment
  118.      * @param userId Unique identifier representing the user
  119.      * @param attributes User attributes used for segmentation
  120.      * @param promise React Native Promise used to return the variation
  121.      * Returns: variationName or null if SDK is not initialized or an error occurs
  122.      */
  123.     @ReactMethod
  124.     fun getVariationName(
  125.         experimentName: String,
  126.         userId: String,
  127.         attributes: ReadableMap,
  128.         promise: Promise
  129.     ) {
  130.         val clientInstance = client
  131.         if (clientInstance == null) {
  132.             println("$LOG_TAG getVariationName called before SDK initialization")
  133.             promise.resolve(null)
  134.             return
  135.         }
  136.         val attrs = readableMapToHashMap(attributes)
  137.         try {
  138.             val variationName = clientInstance.getVariationName(experimentName, userId, attrs)
  139.             println("$LOG_TAG Variation retrieved: $variationName")
  140.             promise.resolve(variationName)
  141.         } catch (e: Exception) {
  142.             println("$LOG_TAG getVariationName error: ${e.message}")
  143.             promise.resolve(null)
  144.         }
  145.     }
  146.  
  147.     /**
  148.      * Tracks a goal conversion for a PageSense experiment.
  149.      * @param experimentName Name of the experiment
  150.      * @param userId Unique identifier representing the user
  151.      * @param goalName Name of the goal configured in PageSense
  152.      * @param attributes User attributes associated with the event
  153.      */
  154.     @ReactMethod
  155.     fun trackGoal(
  156.         experimentName: String,
  157.         userId: String,
  158.         goalName: String,
  159.         attributes: ReadableMap
  160.     ) {
  161.         val clientInstance = client ?: run {
  162.             println("$LOG_TAG trackGoal called before SDK initialization")
  163.             return
  164.         }
  165.         val attrs = readableMapToHashMap(attributes)
  166.         try {
  167.             clientInstance.trackGoal(experimentName, userId, goalName, attrs)
  168.             println("$LOG_TAG Goal tracked: $goalName")
  169.         } catch (e: Exception) {
  170.             println("$LOG_TAG trackGoal error: ${e.message}")
  171.         }
  172.     }
  173.  
  174.     /**
  175.      * Converts React Native ReadableMap into a HashMap<String,String>.
  176.      * @param map ReadableMap received from React Native
  177.      * Returns: HashMap<String,String> containing user attributes
  178.      */
  179.     private fun readableMapToHashMap(map: ReadableMap): HashMap<String, String> {
  180.         val hashMap = HashMap<String, String>()
  181.         val iterator = map.keySetIterator()
  182.         while (iterator.hasNextKey()) {
  183.             val key = iterator.nextKey()
  184.             val value = map.getString(key)
  185.             if (value != null) { hashMap[key] = value }
  186.         }
  187.         return hashMap
  188.     }
  189. }

ii. Creating the React Native Package File

Create the React Native Package file to integrate the Android native module with the React Native application layer. This file exposes the Android Kotlin module to React Native, enabling communication between the native Android implementation and the React Native codebase. This file should be placed within the Android application Java Source Package directory located inside the android folder of the React Native project’s root directory.

Example: /ProjectRoot/android/app/src/main/java/com/sampleapp/PageSenseSDKPackage.kt

  1. package com.sampleapp
  2.  
  3. import com.facebook.react.ReactPackage
  4. import com.facebook.react.bridge.NativeModule
  5. import com.facebook.react.bridge.ReactApplicationContext
  6. import com.facebook.react.uimanager.ViewManager
  7.  
  8. /**
  9.  * React Native Package responsible for registering the PageSenseSDKModule.
  10.  * The module becomes accessible through: NativeModules.PageSenseSDKModule
  11.  */
  12. class PageSenseSDKPackage : ReactPackage {
  13.  
  14.     companion object {
  15.         private const val LOG_TAG = "[PageSenseSDK]"
  16.     }
  17.  
  18.     /**
  19.      * Registers native modules that should be accessible from React Native.
  20.      * @param reactContext ReactApplicationContext instance provided by React Native.
  21.      * Returns: List<NativeModule> to be registered with the React Native bridge.
  22.      */
  23.     override fun createNativeModules(
  24.         reactContext: ReactApplicationContext
  25.     ): List<NativeModule> {
  26.         println("$LOG_TAG Registering PageSenseSDKModule with React Native bridge")
  27.         return listOf(PageSenseSDKModule(reactContext))
  28.     }
  29.  
  30.     /**
  31.      * Registers native view managers with React Native.
  32.      * @param reactContext ReactApplicationContext instance.
  33.      * Returns: List<ViewManager<*, *>> exposed to React Native.
  34.      */
  35.     override fun createViewManagers(
  36.         reactContext: ReactApplicationContext
  37.     ): List<ViewManager<*, *>> {
  38.         println("$LOG_TAG No native view managers registered for PageSense")
  39.         return emptyList()
  40.     }
  41. }

iii. Registering the React Native Package

The React Native package must be registered in the Android application. Edit the Android file MainApplication.kt and add the package to the list of React Native packages. This makes the PageSenseSDK module accessible to React Native application.

  1. package com.sampleapp
  2.  
  3. import android.app.Application
  4. import com.facebook.react.PackageList
  5. import com.facebook.react.ReactApplication
  6. import com.facebook.react.ReactHost
  7. import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
  8. import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
  9. import com.sampleapp.PageSenseSDKPackage
  10.  
  11. class MainApplication : Application(), ReactApplication {
  12.  
  13.     override val reactHost: ReactHost by lazy {
  14.         getDefaultReactHost(
  15.             context = applicationContext,
  16.             packageList =
  17.                 PackageList(this).packages.apply {
  18.                     // Packages that cannot be autolinked yet can be added manually here
  19.                     add(PageSenseSDKPackage())
  20.                 },
  21.         )
  22.     }
  23.  
  24.     override fun onCreate() {
  25.         super.onCreate()
  26.         loadReactNative(this)
  27.     }
  28. }

Creating the React Native Wrapper

Create the React Native wrapper file to enable interaction between the React Native application and the native PageSense SDK implementation. This wrapper acts as an interface layer that exposes the native module methods to the JavaScript/TypeScript code in the React Native application.
The wrapper file should be created at the following path within the project structure from project root directory: src/sdk/PageSenseSDKWrapper.ts
  1. import { NativeModules } from 'react-native';
  2.  
  3. // Extract the native module exposed through the React Native bridge.
  4. const { PageSenseSDKModule } = NativeModules;
  5.  
  6. /**
  7.  * PageSenseSDKWrapper provides a JavaScript interface for interacting
  8.  * with the PageSense SDK through the React Native Native Bridge.
  9.  */
  10. class PageSenseSDKWrapper {
  11.  
  12.     /**
  13.      * Initialise the PageSense SDK. Should be called once during application startup.
  14.      * @param {string} accountId - PageSense account identifier.
  15.      * @param {string} sdkKey - SDK key used for authentication.
  16.      * @param {string} projectName - Name of the PageSense project.
  17.      * @returns {Promise<boolean>} true = initialized successfully, false = failed
  18.      */
  19.     async initialiseSDK(accountId, sdkKey, projectName) {
  20.         try {
  21.             this._validateString(accountId, 'accountId');
  22.             this._validateString(sdkKey, 'sdkKey');
  23.             this._validateString(projectName, 'projectName');
  24.             const result = await PageSenseSDKModule.initialiseSDK(accountId, sdkKey, projectName);
  25.             return result === true;
  26.         } catch (error) {
  27.             console.log('[PageSenseSDK] initialiseSDK error:', error);
  28.             return false;
  29.         }
  30.     }
  31.  
  32.     /**
  33.      * Activates an experiment and returns the assigned variation.
  34.      * @param {string} experimentName - Name of the experiment.
  35.      * @param {string} userId - Unique user identifier.
  36.      * @param {Object.<string, string>} [attributes] - Optional user attributes.
  37.      * @returns {Promise<string|null>} variation name or null if no variation or error
  38.      */
  39.     async activateExperiment(experimentName, userId, attributes) {
  40.         try {
  41.             this._validateString(experimentName, 'experimentName');
  42.             this._validateString(userId, 'userId');
  43.             this._validateAttributes(attributes);
  44.             const safeAttributes = attributes || {};
  45.             const variation = await PageSenseSDKModule.activateExperiment(
  46.                 experimentName, userId, safeAttributes
  47.             );
  48.             return variation ?? null;
  49.         } catch (error) {
  50.             console.log('[PageSenseSDK] activateExperiment error:', error);
  51.             return null;
  52.         }
  53.     }
  54.  
  55.     /**
  56.      * Retrieves the variation name without activating the experiment.
  57.      * @param {string} experimentName - Name of the experiment.
  58.      * @param {string} userId - Unique user identifier.
  59.      * @param {Object.<string, string>} [attributes] - Optional user attributes.
  60.      * @returns {Promise<string|null>} variation name or null if not available or error
  61.      */
  62.     async getVariationName(experimentName, userId, attributes) {
  63.         try {
  64.             this._validateString(experimentName, 'experimentName');
  65.             this._validateString(userId, 'userId');
  66.             this._validateAttributes(attributes);
  67.             const safeAttributes = attributes || {};
  68.             const variation = await PageSenseSDKModule.getVariationName(
  69.                 experimentName, userId, safeAttributes
  70.             );
  71.             return variation ?? null;
  72.         } catch (error) {
  73.             console.log('[PageSenseSDK] getVariationName error:', error);
  74.             return null;
  75.         }
  76.     }
  77.  
  78.     /**
  79.      * Tracks a goal event for an experiment.
  80.      * @param {string} experimentName - Name of the experiment.
  81.      * @param {string} userId - Unique user identifier.
  82.      * @param {string} goalName - Name of the goal.
  83.      * @param {Object.<string, string>} [attributes] - Optional goal attributes.
  84.      * @returns {void}
  85.      */
  86.     trackGoal(experimentName, userId, goalName, attributes) {
  87.         try {
  88.             this._validateString(experimentName, 'experimentName');
  89.             this._validateString(userId, 'userId');
  90.             this._validateString(goalName, 'goalName');
  91.             this._validateAttributes(attributes);
  92.             const safeAttributes = attributes || {};
  93.             PageSenseSDKModule.trackGoal(experimentName, userId, goalName, safeAttributes);
  94.         } catch (error) {
  95.             console.log('[PageSenseSDK] trackGoal error:', error);
  96.         }
  97.     }
  98.  
  99.     /**
  100.      * Validates whether the given parameter is a non-empty string.
  101.      * @private
  102.      * @throws {Error} if param is not a non-empty string
  103.      */
  104.     _validateString(param, paramName) {
  105.         if (typeof param !== 'string' || param.trim() === '') {
  106.             throw new Error(`[PageSenseSDK] Invalid ${paramName}: must be a non-empty string`);
  107.         }
  108.     }
  109.  
  110.     /**
  111.      * Validates the attributes object to ensure it is a valid key-value pair structure.
  112.      * @private
  113.      * @throws {Error} if attributes is invalid
  114.      */
  115.     _validateAttributes(attributes) {
  116.         if (attributes === undefined || attributes == null) return;
  117.         if (typeof attributes !== 'object' || Array.isArray(attributes)) {
  118.             throw new Error('[PageSenseSDK] attributes must be a key-value object');
  119.         }
  120.         for (const key in attributes) {
  121.             if (typeof key !== 'string' || key.trim() === '') {
  122.                 throw new Error('[PageSenseSDK] Invalid attribute key: must be a non-empty string');
  123.             }
  124.             const value = attributes[key];
  125.             if (typeof value !== 'string') {
  126.                 throw new Error(`[PageSenseSDK] Invalid value for attribute "${key}". Allowed type: string`);
  127.             }
  128.         }
  129.     }
  130. }
  131.  
  132. // Export a singleton instance
  133. export default new PageSenseSDKWrapper();

Initializing the SDK

PageSense SDK must be initialised a single time during the application’s lifecycle, ideally when the app launches. For example, you can initialise the SDK in the main entry point of the React Native application, such as App.jsx or App.js, before rendering any components or executing experiment-related logic. This approach guarantees that the SDK is fully set up and available whenever it is needed within the app.
  1. /**
  2.  * Root application component initializes the PageSense SDK when the app starts.
  3.  * UI is rendered only after SDK initialization completes.
  4.  */
  5. export default function App() {
  6.  
  7.     const [sdkReady, setSdkReady] = useState(false);
  8.  
  9.     useEffect(() => {
  10.         async function initializeSDK() {
  11.             try {
  12.                 const result = await PageSenseSDKWrapper.initialiseSDK(
  13.                     'PAGESENSE_ACCOUNT_ID',   // PageSense Account ID
  14.                     'PAGESENSE_SDK_KEY',       // SDK Key
  15.                     'PAGESENSE_PROJECT_NAME',  // Project Name
  16.                 );
  17.                 console.log('PageSense SDK initialized successfully:', result);
  18.                 setSdkReady(true);
  19.             } catch (error) {
  20.                 console.log('PageSense SDK initialization error:', error);
  21.                 // Still allow app to proceed even if SDK fails
  22.                 setSdkReady(true);
  23.             }
  24.         }
  25.         initializeSDK();
  26.     }, []);
  27.  
  28.     if (!sdkReady) {
  29.         return (
  30.             <View style={styles.loadingContainer}>
  31.                 <ActivityIndicator size="large" />
  32.                 <Text style={styles.loadingText}>Initializing App...</Text>
  33.             </View>
  34.         );
  35.     }
  36.  
  37.     return (
  38.         ....Application UI Implementation
  39.     );
  40. }

 Activating an Experiment

The experiment should be executed when the screen is loaded to ensure that users are allocated to the correct variation and any relevant changes in the user interface or functionality are applied immediately. This ensures that the experiment is triggered as soon as the screen becomes active, providing consistent behaviour and accurate variation assignment. For example, in a React Native component, you can call the experiment inside a lifecycle method such as useEffect in App.jsx or App.js or any relevant screen component, so it runs once when the screen mounts:
  1. /**
  2.  * Executes the experiment and applies the appropriate UI variation.
  3.  */
  4. try {
  5.     const variation = await PageSenseSDKWrapper.activateExperiment(
  6.         'EXPERIMENT_NAME',  // Experiment name
  7.         'USER_ID',          // Unique user identifier
  8.         'USER_ATTIBUTES'    // Optional user attributes
  9.     );
  10.  
  11.     console.log('Experiment Variation:', variation);
  12.  
  13.     switch (variation) {
  14.         case 'Original':
  15.             setButtonColor('#4F46E5'); // Default purple color
  16.             break;
  17.         case 'Variation 1':
  18.             setButtonColor('#EF4444'); // Red button
  19.             break;
  20.         case 'Variation 2':
  21.             setButtonColor('#16A34A'); // Green button
  22.             break;
  23.         case 'Variation 3':
  24.             setButtonColor('#F59E0B'); // Orange button
  25.             break;
  26.         default:
  27.             setButtonColor('#6B7280'); // Neutral grey fallback
  28.     }
  29. } catch (e) {
  30.     console.log('Experiment error', e);
  31.     setButtonColor('#6B7280');
  32. }

Tracking Goals

Goal tracking should be performed immediately after a user completes a specific action or event within the application. This ensures that conversions, interactions, or other key performance indicators are recorded in real time, providing accurate and timely data for analysis of experiment effectiveness. For example, after a user taps a button, submits a form, or completes a purchase, the corresponding goal should be sent to the PageSense SDK for tracking.
  1. /**
  2.  * Tracks a goal event for the specified experiment.
  3.  */
  4. PageSenseSDKWrapper.trackGoal(
  5.     'EXPERIMENT_NAME',  // Experiment associated with the goal event
  6.     'USER_ID',          // Unique user identifier
  7.     'GOAL_NAME',        // Goal name defined in PageSense
  8.     'USER_ATTIBUTES');  // Optional user attributes

Best Practices

  1. Ensure the SDK is initialised only once during the application lifecycle for a project.
  2. Initialize the SDK when the application starts to ensure experiments are available immediately.
  3. Applications should not block UI rendering if SDK initialization fails.
  4. Use a fallback UI if the SDK initialization fails.
  5. PageSenseClient instance should exist for the entire lifetime of the application session.
  6. Use a consistent user ID across the application session to ensure proper experiment tracking.
  7. Handle Null Variations. Always fallback to a safe default UI.
  8. Native modules should not store UI state and should only store SDK-related objects.
  9. Goals should be triggered after the specific user action or events completes successfully.
  10. Use structured logging to assist with debugging during development.

Summary

This guide explained how to integrate the PageSense Android SDK with React Native applications using the React Native Native Bridge.
The integration process involves:
  1. Installing the PageSense Android SDK
  2. Creating the native bridge module
  3. Registering the React Native package
  4. Implementing the JavaScript wrapper
  5. Initializing the SDK
  6. Activating experiments
  7. Tracking experiment goals
Once integrated, React Native applications can fully leverage PageSense FullStack A/B testing capabilities while maintaining a clean and scalable architecture.




We’ve designed this documentation to guide you every step of the way. If you need further assistance or have any questions, don’t hesitate to contact us at support@zohopagesense.com - we’re always here to help!