React Native SoluCX Widget
React Native package for displaying SoluCX satisfaction surveys (NPS, CSAT, CES) inside your app.
Overview
The widget loads the survey via WebView and automatically manages display options (type, height, frequency, sampling, and suppression rules). You only need to provide customer/transaction data and the SDK handles the rest.
Display modes: Bottom, Top, Modal, and Inline.
Installation
In React Native without Expo (Bare React Native) projects, you must manually install native modules after package installation. In Expo Managed Workflow projects, native module installation is handled automatically.
Quick Start
There are two ways to integrate the widget. Choose the one that best fits your project:
Function Integration (recommended)
This is the most flexible approach. You mount a SoluCXWidgetHost once at the app root and trigger the widget from anywhere using SoluCXWidget.create(...).show().
Step 1: Mount SoluCXWidgetHost at the root
SoluCXWidgetHost is the container that renders the widget. It must be mounted once, in the root component.
Step 2: Trigger the widget from any screen
Call SoluCXWidget.create() whenever you want to display the survey. Just provide the key, display type, and customer data — the SDK fetches the remaining settings (height, frequency, sampling, etc.) automatically from the journey panel.
That's it. The SDK calls the API, applies the display rules configured in the panel, and decides whether the widget should appear or not.
Step 3 (optional): Override options locally
If you need to override the panel settings for a specific case, use .setOptions(). When called, the widget ignores the remote configuration and uses only the provided values.
Summary: setOptions() is optional. Without it, the widget fetches everything from the API. With it, you control settings locally.
Component Integration (alternative)
If you prefer to control display through JSX itself, use SoluCXWidgetView directly. In this mode, you don't need to mount SoluCXWidgetHost — the component is self-contained.
The component is rendered at the exact position where it is declared in JSX. Ideal for inline mode.
Playground
Display Modes
| Mode | Behavior |
|---|---|
bottom | Fixed at the bottom of the screen (default) |
top | Fixed at the top of the screen |
modal | Centered overlay that blocks interaction with the background |
inline | Inserted into the layout flow, at the position where the component is declared |
Only inline mode respects position in JSX. The bottom, top, and modal modes always appear at their fixed position, regardless of where the component is declared.
Complete Example with Callbacks
This example shows every stage of the widget lifecycle — from preparation to closure:
Dismiss the widget programmatically
API
SoluCXWidget (main class)
Builder methods (builder pattern)
| Method | Description |
|---|---|
SoluCXWidget.create(soluCXKey) | Creates a new widget instance |
.setType(type) | Sets the type: 'bottom', 'top', 'modal', 'inline' |
.setData(data) | Sets user/transaction data |
.setOptions(options) | Sets local widget options (optional — if not called, fetches from API) |
.setCallbacks(callbacks) | Sets event callbacks |
.show() | Displays the widget |
Static methods
| Method | Description |
|---|---|
SoluCXWidget.dismiss() | Closes the widget programmatically |
Instance methods (log management)
Use builder data (instanceKey, journey, userId) to access logs for the correct combination.
| Method | Description |
|---|---|
.getWidgetLogs() | Returns widget logs |
.overrideTimestamp(field, date) | Overrides an event timestamp (debug/testing) |
.clearWidgetLogs() | Clears all logs |
Storage isolation: Logs are isolated by instanceKey:journey:userId. An app with 2 widgets from different journeys will have independent data. The userId is optional — when present, logs are per user; when absent, they are shared on the device.
WidgetData
Data sent to identify the customer and the survey context.
The customer_id field is used by the SDK to identify the user and apply display rules per person. If not provided, the SDK falls back to document, email. We recommend always providing customer_id for correct per-user rules.
WidgetOptions
Configures widget display options (type, height, frequency, sampling, and suppression rules). Optional — if setOptions() is not called, the widget fetches these settings automatically from the API (journey configuration panel).
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | When false, blocks immediately without validating other rules |
samplingPercentage | number | 100 | Percentage of users who will see the widget (0-100). 100 = everyone sees it. 0 = nobody sees it |
type | string | 'bottom' | Display type: 'bottom', 'top', 'modal', 'inline'. Can be configured remotely via the journey panel |
height | number | 600 | Fixed widget height in pixels. 0 = automatic. Can be configured remotely via the journey panel |
maxAttemptsAfterDismiss | number | 0 | Maximum number of displays per experience. 0 = no limit. Resets when the experience ID changes |
waitDaysAfterWidgetDisplayAttempt | number | 0 | Days to wait after a blocked display attempt |
waitDaysAfterWidgetFirstAccess | number | 0 | Days to wait after the user's first access to the journey |
waitDaysAfterWidgetDisplay | number | 0 | Days to wait after any widget display |
waitDaysAfterWidgetDismiss | number | 0 | Days to wait after the user closes the widget |
waitDaysAfterWidgetSubmit | number | 0 | Days to wait after full survey submission |
waitDaysAfterWidgetPartialSubmit | number | 0 | Days to wait after partial survey submission |
Tip: A value of 0 for wait days fields = no restriction (the field does not block). Settings can be managed remotely via the journey panel — this way you don't need to update the app to change the rules.
Important about Quarantine: The widget's quarantine control is based on the customer's browser cookies. This means that the quarantine is not 100% guaranteed, as the user can clear cookies, use different browsers, different devices, or incognito/private browsing mode. For scenarios that require strict quarantine control, we recommend implementing additional validations on the server side.
WidgetCallbacks
Functions called at each stage of the widget lifecycle. All are optional.
Lifecycle
When .show() is called, the SDK executes the following steps:
- Checks local options (enabled, sampling, attempts, intervals)
- If blocked: calls
onBlockedwith the reason and the widget does not appear - If allowed: requests the survey URL from the SoluCX API (preflight)
- Calls
onPreOpen→ displays the widget → callsonOpened - During the survey, the user interacts with the form
- When the user answers the main rating (NPS/CSAT/CES): calls
onPartialCompleted - When the user completes the entire survey: calls
onCompleted - When closed (by the user or automatically): calls
onClosed - If an error occurs at any step: calls
onError
Display rule validation is performed locally, before calling the API, to reduce server load.
Callback descriptions
| Callback | Parameter | When it is called |
|---|---|---|
onPreOpen | userId: string | Right before the widget is displayed, after display rule validation passes |
onOpened | userId: string | When the widget is displayed on screen |
onBlocked | reason: BlockReason | When display rules prevent the widget from being displayed. See BlockReason |
onClosed | - | When the widget is closed, either by the user or automatically |
onCompleted | userId: string | When the user completes the entire survey (all steps) |
onPartialCompleted | userId: string | When the user answers the main rating (NPS/CSAT/CES) |
onError | message: string | When a survey error occurs (survey expired, already rated, network failure) |
onPageChanged | page: string | When the user navigates between pages (Flex Survey) |
onQuestionAnswered | - | When the user answers a question (Flex Survey) |
onResize | height: string | When the widget height changes |
BlockReason
Reasons why display rules prevent the widget from being displayed. These blocks are local (on the device) and do not represent API errors.
| Reason | Description |
|---|---|
BLOCKED_BY_DISABLED | Widget is disabled (enabled: false) |
BLOCKED_BY_SAMPLING | User was not selected by sampling (samplingPercentage) |
BLOCKED_BY_TRANSACTION_ALREADY_ANSWERED | Transaction was already answered previously |
BLOCKED_BY_MAX_ATTEMPTS | Maximum number of attempts reached for this experience |
BLOCKED_BY_WIDGET_DISPLAY_ATTEMPT_INTERVAL | Within the interval after a display attempt |
BLOCKED_BY_WIDGET_FIRST_ACCESS_INTERVAL | Within the interval after first access to the journey |
BLOCKED_BY_WIDGET_DISPLAY_INTERVAL | Within the interval after a display |
BLOCKED_BY_WIDGET_DISMISS_INTERVAL | Within the interval after dismissal |
BLOCKED_BY_WIDGET_SUBMIT_INTERVAL | Within the interval after full submission |
BLOCKED_BY_WIDGET_PARTIAL_SUBMIT_INTERVAL | Within the interval after partial submission |
Multiple Widgets
Each combination of instanceKey + journey + userId operates with independent logs:
Troubleshooting
Widget does not appear
- Check if the SoluCX key (
soluCXKey) is valid - Check internet connectivity
- Use the
onBlockedcallback to check if display rules are preventing display - Use the
onErrorcallback to check for survey errors
Callbacks are not called
- Confirm the
callbacksobject is being passed via.setCallbacks()or as a prop onSoluCXWidgetView - Check the console for WebView error messages
Broken layout
- Set a fixed height via
setOptions({ height: 600 })if automatic resizing does not work well in your layout - For
inlinemode, make sure the parent component has enough space
Compatibility
| Version | React Native | Expo | iOS | Android |
|---|---|---|---|---|
| 2.x | 0.70+ | 50+ | 11+ | API 21+ |
License
This package is proprietary to SoluCX. Use is restricted to licensed SoluCX platform customers.