React Native

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}" />;

It's all comes with TypeScript support, dont worry.

Parameters

Refer to the full Thanks API configuration documentation for more.

Advanced usage

The important parts are:

  • providing a customer email. Strictly sha256 hash will be transferred during the widget lifecycle. No sensitive information leaves your application without your permission.
  • giving permission to send Personal Information to improve efficiency of communications
    • subject and info can be used to decide what information to send
      • subject can be notification or autofill of visible UI elements
    • info.token is a unique identifier for the request and can be used to trace PII flow further in our systems
  • keywords, category and items are used to fine-tune ads to display
<ThanksWidget
  partnerId="{your partner id}"
  // information for the first scren
  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 above
  onLinkClick={(url) => {
		// Keep deep links in-your-own-app: render content inside the Thanks 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, Thanks will NOT call Linking.openURL
		}
		// Everything else: let Thanks open it (equivalent to
		// `Linking.openURL(url)`).
				return true;
	}}
  // ... other configuration parameters above
/>

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, e.g. 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 YourThanksPage = () => {
  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 following 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
  }}
/>

Error codes:

  • WebviewError: WebView failure (network, HTTP, or page crash/termination).
  • StartupTimeout: Widget did not become ready within the startup time limit.
  • LoadingError: Loading failed before the widget was ready.
  • TargetResolveFailed: Failed to resolve the widget or block URL target.
  • CooldownFetchFailed: Failed to fetch cooldown state (e.g. cooldown).
  • PiiRequestFailed: onPersonalInformationRequest callback threw or rejected.

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 ensure 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