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:
In the context of the PageSense Android SDK, the bridge enables the React Native application to call Android SDK methods such as:
These native methods are implemented in Kotlin and exposed to React Native through the React Native bridge API.
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
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
- Node.js
- npm
- Watchman
- React Native CLI
Android Development Tools
- Android Studio
- Android SDK
- Gradle
- 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 RepositoryAdd 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 DependencyAdd 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:
- 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:
- ReactContextBaseJavaModule
- ReactMethod
- PromiseReadableMap
- ReactPackage
- 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:
- ReactContextBaseJavaModule
- ReactMethod
- Promise
- ReadableMap
- ReactPackage
- 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:
- Kotlin Native Module Implementation
- React Native Package Implementation
- React Native Package Registration
Android directly exposes native methods using the React Native bridge APIs.
i. Creating the Kotlin Native Module
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.
- package com.sampleapp
-
- 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.sampleapp.PageSenseSDKPackage
-
- 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
- add(PageSenseSDKPackage())
- },
- )
- }
-
- override fun onCreate() {
- super.onCreate()
- loadReactNative(this)
- }
- }
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
- import { NativeModules } from 'react-native';
-
- // Extract the native module exposed through the React Native bridge.
- const { PageSenseSDKModule } = NativeModules;
-
- /**
- * PageSenseSDKWrapper provides a JavaScript interface for interacting
- * with the PageSense SDK through the React Native Native Bridge.
- */
- class PageSenseSDKWrapper {
-
- /**
- * Initialise the PageSense SDK. Should be called once during application startup.
- * @param {string} accountId - PageSense account identifier.
- * @param {string} sdkKey - SDK key used for authentication.
- * @param {string} projectName - Name of the PageSense project.
- * @returns {Promise<boolean>} true = initialized successfully, false = failed
- */
- async initialiseSDK(accountId, sdkKey, projectName) {
- try {
- this._validateString(accountId, 'accountId');
- this._validateString(sdkKey, 'sdkKey');
- this._validateString(projectName, 'projectName');
- const result = await PageSenseSDKModule.initialiseSDK(accountId, sdkKey, projectName);
- return result === true;
- } catch (error) {
- console.log('[PageSenseSDK] initialiseSDK error:', error);
- return false;
- }
- }
-
- /**
- * Activates an experiment and returns the assigned variation.
- * @param {string} experimentName - Name of the experiment.
- * @param {string} userId - Unique user identifier.
- * @param {Object.<string, string>} [attributes] - Optional user attributes.
- * @returns {Promise<string|null>} variation name or null if no variation or error
- */
- async activateExperiment(experimentName, userId, attributes) {
- try {
- this._validateString(experimentName, 'experimentName');
- this._validateString(userId, 'userId');
- this._validateAttributes(attributes);
- const safeAttributes = attributes || {};
- const variation = await PageSenseSDKModule.activateExperiment(
- experimentName, userId, safeAttributes
- );
- return variation ?? null;
- } catch (error) {
- console.log('[PageSenseSDK] activateExperiment error:', error);
- return null;
- }
- }
-
- /**
- * Retrieves the variation name without activating the experiment.
- * @param {string} experimentName - Name of the experiment.
- * @param {string} userId - Unique user identifier.
- * @param {Object.<string, string>} [attributes] - Optional user attributes.
- * @returns {Promise<string|null>} variation name or null if not available or error
- */
- async getVariationName(experimentName, userId, attributes) {
- try {
- this._validateString(experimentName, 'experimentName');
- this._validateString(userId, 'userId');
- this._validateAttributes(attributes);
- const safeAttributes = attributes || {};
- const variation = await PageSenseSDKModule.getVariationName(
- experimentName, userId, safeAttributes
- );
- return variation ?? null;
- } catch (error) {
- console.log('[PageSenseSDK] getVariationName error:', error);
- return null;
- }
- }
-
- /**
- * Tracks a goal event for an experiment.
- * @param {string} experimentName - Name of the experiment.
- * @param {string} userId - Unique user identifier.
- * @param {string} goalName - Name of the goal.
- * @param {Object.<string, string>} [attributes] - Optional goal attributes.
- * @returns {void}
- */
- trackGoal(experimentName, userId, goalName, attributes) {
- try {
- this._validateString(experimentName, 'experimentName');
- this._validateString(userId, 'userId');
- this._validateString(goalName, 'goalName');
- this._validateAttributes(attributes);
- const safeAttributes = attributes || {};
- PageSenseSDKModule.trackGoal(experimentName, userId, goalName, safeAttributes);
- } catch (error) {
- console.log('[PageSenseSDK] trackGoal error:', error);
- }
- }
-
- /**
- * Validates whether the given parameter is a non-empty string.
- * @private
- * @throws {Error} if param is not a non-empty string
- */
- _validateString(param, paramName) {
- if (typeof param !== 'string' || param.trim() === '') {
- throw new Error(`[PageSenseSDK] Invalid ${paramName}: must be a non-empty string`);
- }
- }
-
- /**
- * Validates the attributes object to ensure it is a valid key-value pair structure.
- * @private
- * @throws {Error} if attributes is invalid
- */
- _validateAttributes(attributes) {
- if (attributes === undefined || attributes == null) return;
- if (typeof attributes !== 'object' || Array.isArray(attributes)) {
- throw new Error('[PageSenseSDK] attributes must be a key-value object');
- }
- for (const key in attributes) {
- if (typeof key !== 'string' || key.trim() === '') {
- throw new Error('[PageSenseSDK] Invalid attribute key: must be a non-empty string');
- }
- const value = attributes[key];
- if (typeof value !== 'string') {
- throw new Error(`[PageSenseSDK] Invalid value for attribute "${key}". Allowed type: string`);
- }
- }
- }
- }
-
- // Export a singleton instance
- 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.
- /**
- * Root application component initializes the PageSense SDK when the app starts.
- * UI is rendered only after SDK initialization completes.
- */
- export default function App() {
-
- const [sdkReady, setSdkReady] = useState(false);
-
- useEffect(() => {
- async function initializeSDK() {
- try {
- const result = await PageSenseSDKWrapper.initialiseSDK(
- 'PAGESENSE_ACCOUNT_ID', // PageSense Account ID
- 'PAGESENSE_SDK_KEY', // SDK Key
- 'PAGESENSE_PROJECT_NAME', // Project Name
- );
- console.log('PageSense SDK initialized successfully:', result);
- setSdkReady(true);
- } catch (error) {
- console.log('PageSense SDK initialization error:', error);
- // Still allow app to proceed even if SDK fails
- setSdkReady(true);
- }
- }
- initializeSDK();
- }, []);
-
- if (!sdkReady) {
- return (
- <View style={styles.loadingContainer}>
- <ActivityIndicator size="large" />
- <Text style={styles.loadingText}>Initializing App...</Text>
- </View>
- );
- }
-
- return (
- ....Application UI Implementation
- );
- }
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:
- /**
- * Executes the experiment and applies the appropriate UI variation.
- */
- try {
- const variation = await PageSenseSDKWrapper.activateExperiment(
- 'EXPERIMENT_NAME', // Experiment name
- 'USER_ID', // Unique user identifier
- 'USER_ATTIBUTES' // Optional user attributes
- );
-
- console.log('Experiment Variation:', variation);
-
- switch (variation) {
- case 'Original':
- setButtonColor('#4F46E5'); // Default purple color
- break;
- case 'Variation 1':
- setButtonColor('#EF4444'); // Red button
- break;
- case 'Variation 2':
- setButtonColor('#16A34A'); // Green button
- break;
- case 'Variation 3':
- setButtonColor('#F59E0B'); // Orange button
- break;
- default:
- setButtonColor('#6B7280'); // Neutral grey fallback
- }
- } catch (e) {
- console.log('Experiment error', e);
- setButtonColor('#6B7280');
- }
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.
- /**
- * Tracks a goal event for the specified experiment.
- */
- PageSenseSDKWrapper.trackGoal(
- 'EXPERIMENT_NAME', // Experiment associated with the goal event
- 'USER_ID', // Unique user identifier
- 'GOAL_NAME', // Goal name defined in PageSense
- 'USER_ATTIBUTES'); // Optional user attributes
Best Practices
- Ensure the SDK is initialised only once during the application lifecycle for a project.
- Initialize the SDK when the application starts to ensure experiments are available immediately.
- Applications should not block UI rendering if SDK initialization fails.
- Use a fallback UI if the SDK initialization fails.
- PageSenseClient instance should exist for the entire lifetime of the application session.
- Use a consistent user ID across the application session to ensure proper experiment tracking.
- Handle Null Variations. Always fallback to a safe default UI.
- Native modules should not store UI state and should only store SDK-related objects.
- Goals should be triggered after the specific user action or events completes successfully.
- 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:
- Installing the PageSense Android SDK
- Creating the native bridge module
- Registering the React Native package
- Implementing the JavaScript wrapper
- Initializing the SDK
- Activating experiments
- 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!