React Native

The @thanksjs/react-native-webview package and its ThanksWidget, ThanksBlock, and ThanksWidgetController components still use the Thanks name. You don't need to change your integration.

Installation

yarn add @thanksjs/react-native-webview react-native-webview

Usage

import { ThanksWidget } from '@thanksjs/react-native-webview';

/// somewhere in your code
<ThanksWidget partnerId="{your partner id}" />;

Everything ships with TypeScript types.

Parameters

Refer to the full configuration parameters documentation.

Advanced usage

The important parts are:

  • Providing a customer email. Only a sha256 hash is transferred during the widget lifecycle. No sensitive information leaves your application without your permission.
  • Giving permission to send personal information to improve the efficiency of communications
    • subject and info can be used to decide what information to send
      • subject can be relevance, notification, or autofill
    • info.token is a unique identifier for the request and can be used to trace PII flow further in our systems
  • keywords, categories, and items are used to fine-tune the ads displayed
<ThanksWidget
  partnerId="{your partner id}"
  // information for the first screen
  statusText="Your order has been confirmed"
  emailHash={{ sha256: customersEmailHash }}
  // or
  email={customerEmail}
  onPersonalInformationRequest={(subject, info) => {
    return {
      email,
      firstName: 'TestUser',
    };
  }}
  onDisplay={() => {
    console.log('widget displayed');
  }}
  onClose={() => {
    console.log('widget closed');
  }}
  keywords={['violet', 'roses', 'blueberry']}
  items={[
    {
      name: 'Flatwhite',
      value: 4.0,
      currency: 'AUD',
      quantity: 2,
      type: 'coffee',
      category: 'drinks',
      subcategory: 'australian-coffee',
    },
  ]}
  categories={['lifestyle']}
/>

Using onLinkClick

const [inAppUrl, setInAppUrl] = useState<string | null>(null);

<ThanksWidget
  // ... other configuration parameters
  onLinkClick={(url) => {
    // Keep deep links in your own app: render content inside the Splash experience modal
    // via `children` instead of leaving the app.
    if (url.startsWith('myapp://') || url.includes('offers.myapp.com')) {
      setInAppUrl(url);
      return false; // your app handles it, so the SDK will NOT call Linking.openURL
    }
    // Everything else: let the SDK open it (equivalent to
    // `Linking.openURL(url)`).
    return true;
  }}
/>

Controlling layering and visibility (wrapperComponent)

The React Native SDK wraps the widget WebView in React Native's Modal by default. If you need to control z-index or temporarily move the widget out of frame at runtime (for example, while an in-app browser is open), you can replace the wrapper with your own component.

The wrapper receives { children, onRequestClose, animationType, transparent, isReady }. Because it's a normal React component, it can hold its own state and re-render to reposition the widget without closing it.

import { useState } from 'react';
import { View } from 'react-native';
import { ThanksWidget } from '@thanksjs/react-native-webview';

const CustomWrapper = ({ children, isReady }) => {
  const [behind, setBehind] = useState(false);
  globalThis.__setWidgetBehind = setBehind;

  return (
    <View
      style={{
        position: 'absolute',
        top: 0, left: 0, right: 0, bottom: 0,
        zIndex: behind ? -1 : 9999,
        display: isReady ? 'flex' : 'none',
      }}
    >
      {children}
    </View>
  );
};

<ThanksWidget
  partnerId="YOUR_PARTNER_ID"
  wrapperComponent={CustomWrapper}
  onLinkClick={(url) => {
    globalThis.__setWidgetBehind(true);
    openInAppBrowser(url, {
      onClose: () => globalThis.__setWidgetBehind(false),
    });
    return false;
  }}
/>;

wrapperComponent is also accepted by ThanksWidgetController and thanksWidget.open(). When set in both places, the value passed to open() wins.

Note style.zIndex and offsetTop runtime updates are for the Web SDK only. For the React Native SDK, use wrapperComponent for layering and positioning control. While we plan to improve this, the wrapper pattern is the supported approach.

Indirect usage

Sometimes an event-based solution is easier to manage. In this case, we handle some state management for you.

import { ThanksWidgetController, thanksWidget } from '@thanksjs/react-native-webview';

const YourApplication = () => (
  <>
    {someCode}
    // put AFTER all other views, so it will be displayed on top
    <ThanksWidgetController partnerId="your-id" />
  </>
);

const YourConfirmationPage = () => {
  useEffect(
    () => {
      // ThanksWidgetController will render Widget instantly
      // if ThanksWidgetController is not present this will throw an error
      thanksWidget.open(thanksConfiguration);

      // and hide when you navigate away from the page
      return () => thanksWidget.close();
    },
    [
      // important to keep dependencies free to prevent re-rendering widget
      // as customer's journey will be reset
    ],
  );

  return pageCode;
};

Adding a Block

A Block is a permanently embedded element in your application interface.

If you prefer to embed a ThanksBlock into your application, you can follow the example below:

import { ThanksBlock } from '@thanksjs/react-native-webview';

/// somewhere in your code
<View style={{ width, height }}>
  <ThanksBlock partnerId="YOUR_PARTNER_ID" />
</View>;

In the above example, we wrap the ThanksBlock to set its height and width.

Error handling

You can observe and react to errors in the components with the optional onError prop.

<ThanksWidget
  partnerId="YOUR_PARTNER_ID"
  onError={(err) => {
    // ThanksSdkError
  }}
/>

<ThanksBlock
  partnerId="YOUR_PARTNER_ID"
  slot="main"
  onError={(err) => {
    // ThanksSdkError
  }}
/>

Each error has a code, a message, an origin (widget, block, or controller), and optionally context and originalError (for example, the WebView's native error).

Error codes:

  • WebviewError: WebView failure (network error, or the page crashed or was terminated).
  • HttpError: The widget page returned an HTTP error.
  • StartupTimeout: The widget did not become ready within the startup time limit.
  • LoadingError: Loading failed before the widget was ready.
  • InitialisationFailed: The widget or block could not be initialized.
  • NotEligible: The user is not eligible to see the widget right now (for example, during a cool down period). context.reason has the details, and onClose is called.
  • EligibilityCheckFailed: The eligibility check failed. The widget still displays.
  • PiiRequestFailed: The onPersonalInformationRequest callback threw or rejected.
  • InvalidConfiguration, CooldownFetchFailed: Reserved; not currently raised.

Dealing with 'RNCWebView' could not be found

In this package, we use react-native-webview to embed our Widget within your application.

Since react-native-webview is a native module, you'll need to make sure react-native-webview is properly linked as a native module. For the following examples, we'll assume you use Expo. If you don't use Expo, you can apply the same steps to your workflow.

Option 1: Install react-native-webview

Step 1: Install react-native-webview

npm install react-native-webview

Step 2: Re-run the Prebuild Command

Now, run the prebuild command again:

expo prebuild

Step 3: Run the Custom Development Client

Follow the previous steps to build your custom development client:

expo run:ios

OR

expo run:android

By doing this, we force Expo to add Expo Modules to the iOS Podfile (and build.gradle for Android)

Option 2: Manually install react-native-webview

If you do not want to add another dependency to your project, we recommend you manually link the native modules to your Native app's output.

Step 1: Add react-native-webview to your Podfile.

target 'YourProjectName' do
  # ... (other existing pods)

  pod 'react-native-webview', :path => '../node_modules/react-native-webview'
end

Step 2: Clean the project and reinstall the pods

npx expo prebuild --clean
cd ios && pod install && cd ..

Step 3: Run the development client

npx expo run:ios

Did this page help you?