> ## Documentation Index
> Fetch the complete documentation index at: https://helium-promptless-document-entitlement-management-features.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Quickstart (React Native)

> Integrate Helium into your React Native App.

<Info>
  **Using an older SDK version?**

  If you're upgrading from SDK v0.x, check our [Migration Guide](/sdk-reference/migration-2-to-3) for breaking changes and upgrade instructions.
</Info>

## Background

Get set up with the Helium SDK for React Native. Reach out over your Helium slack channel or email [founders@tryhelium.com](mailto:founders@tryhelium.com) for any questions.

## Installation

Install the SDK using your preferred package manager:

<CodeGroup>
  ```bash Expo 52+
  npx expo install expo-helium
  ```

  ```bash Older Expo / Bare
  npm install @tryheliumai/paywall-sdk-react-native
  # or
  yarn add @tryheliumai/paywall-sdk-react-native

  # [Bare only] then run the following to install the native dependencies
  npx react-native link @tryheliumai/paywall-sdk-react-native
  ```
</CodeGroup>

<Tip>
  We support Expo 49+ but do recommend using Helium with **Expo 53 and up**. If you're on an older version and having issues, ping us - we've got experience with all kinds of versioning, upgrade, and custom build plugin work.
</Tip>

## Configuration

Follow these steps to integrate the SDK.

### HeliumProvider

<Warning>
  HeliumProvider is no longer needed in v3.0+. See our [Migration Guide](/sdk-reference/migration-2-to-3).
</Warning>

### Initialization

Initialize Helium by calling `initialize()` early in your app's lifecycle, typically in your root component.

<Note>
  If you are using **RevenueCat**, check out the example in the next section.
</Note>

<CodeGroup>
  ```tsx Expo 52+
  import { initialize, createCustomPurchaseConfig, HELIUM_CTA_NAMES } from 'expo-helium';

  function App() {
    useEffect(() => {
      initialize({
        apiKey: '<your-helium-api-key>',
        // (Optional) Custom user id, e.g. your amplitude analytics user id.
        customUserId: '<your-custom-user-id>',
        purchaseConfig: createCustomPurchaseConfig({
          makePurchase: async (productId) => {
            // Your purchase logic here
            return { status: 'purchased' };
          },
          restorePurchases: async () => {
            // Your restore logic here
            return true;
          }
        }),

        // Example event handler for paywall events
        onHeliumPaywallEvent: (event: HeliumPaywallEvent) => {
          switch (event.type) {
            case 'paywallOpen':
              break;
            case 'paywallButtonPressed':
              if (event.buttonName === HELIUM_CTA_NAMES.SCHEDULE_CALL) {
                // Handle schedule call
              }
              break;
            case 'purchaseSucceeded':
              // Handle successful purchase
              break;
          }
        },

        // (Optional) Custom user traits
        customUserTraits: {
          "example_trait": "example_value",
        },
      });
    }, []);
  }
  ```

  ```tsx Older Expo / Bare
  import { initialize, createCustomPurchaseConfig, HELIUM_CTA_NAMES } from '@tryheliumai/paywall-sdk-react-native';

  function App() {
    const asyncHeliumInit = async () => {
      initialize({
        apiKey: '<your-helium-api-key>',
        // (Optional) Custom user id, e.g. your amplitude analytics user id.
        customUserId: '<your-custom-user-id>',
        purchaseConfig: createCustomPurchaseConfig({
          makePurchase: async (productId) => {
            // Your purchase logic here
            return { status: 'purchased' };
          },
          restorePurchases: async () => {
            // Your restore logic here
            return true;
          }
        }),

        // Event handler for paywall events
        onHeliumPaywallEvent: (event) => {
          switch (event.type) {
            case 'paywallOpen':
              break;
            case 'paywallButtonPressed':
              if (event.buttonName === HELIUM_CTA_NAMES.SCHEDULE_CALL) {
                // Handle schedule call
              }
              break;
            case 'purchaseSucceeded':
              // Handle successful purchase
              break;
          }
        },

        // (Optional) Custom user traits
        customUserTraits: {
          "example_trait": "example_value",
        },
      });
    };

    useEffect(() => {
      void asyncHeliumInit();
    }, []);
  }
  ```
</CodeGroup>

#### Use RevenueCat with Helium

**Important** Make sure that you've already:

* [Installed](https://www.revenuecat.com/docs/getting-started/installation/expo) RevenueCat (if non-Expo go [here](https://www.revenuecat.com/docs/getting-started/installation/reactnative))
* (Recommended) Configured an [Entitlement](https://www.revenuecat.com/docs/getting-started/entitlements)

<CodeGroup>
  ```tsx Expo 52+
  import { createRevenueCatPurchaseConfig } from "expo-helium/src/revenuecat";

  const asyncHeliumInit = async () => {
    initialize({
      apiKey: '<your-helium-api-key>',
      customUserId: '<your-custom-user-id>', // (optional)
      purchaseConfig: createRevenueCatPurchaseConfig(
        // (optional) Set if you want Helium to initialize RevenueCat for you.
        // Otherwise initialize RevenueCat (`Purchases.configure()`) before initializing Helium
        { apiKey: 'revenue_cat_api_key' }
      ),
      onHeliumPaywallEvent: (event: HeliumPaywallEvent) => {
        switch (event.type) {
          case 'purchaseFailed':
            // Custom logic
            break;
          case 'purchaseSucceeded':
            // Handle a purchase success event
            // e.g. navigate to a premium page
            break;
        }
      },
      revenueCatAppUserId: await Purchases.getAppUserID()
    });
  };

  useEffect(() => {
    void asyncHeliumInit();
  }, []);
  ```

  ```tsx Older Expo / Bare
  import { createRevenueCatPurchaseConfig } from "@tryheliumai/paywall-sdk-react-native/src/revenuecat";

  const asyncHeliumInit = async () => {
    initialize({
      apiKey: '<your-helium-api-key>',
      customUserId: '<your-custom-user-id>', // (optional)
      purchaseConfig: createRevenueCatPurchaseConfig(
        // (optional) Set if you want Helium to initialize RevenueCat for you.
        // Otherwise initialize RevenueCat (`Purchases.configure()`) before initializing Helium
        { apiKey: 'revenue_cat_api_key' }
      ),
      onHeliumPaywallEvent: (event: HeliumPaywallEvent) => {
        switch (event.type) {
          case 'purchaseFailed':
            // Custom logic
            break;
          case 'purchaseSucceeded':
            // Handle a purchase success event
            // e.g. navigate to a premium page
            break;
        }
      },
      revenueCatAppUserId: await Purchases.getAppUserID()
    });
  };

  useEffect(() => {
    void asyncHeliumInit();
  }, []);
  ```
</CodeGroup>

## Presenting Paywalls

Use the `presentUpsell` method to present a paywall. `presentUpsell` takes in `PresentUpsellParams`:

<ResponseField name="PresentUpsellParams" type="method">
  <Expandable title="properties">
    <ResponseField name="triggerName" type="string" required>
      Pass in the trigger for the workflow you configured in the dashboard.
    </ResponseField>

    <ResponseField name="onFallback" type="() => void">
      This will be called when paywall fails to show due to an unsuccessful paywall download or if an invalid trigger is provided. (See Fallbacks section for more details.)
    </ResponseField>

    <ResponseField name="eventHandlers" type="PaywallEventHandlers">
      Handlers that allow you to respond to open, close, dismiss, and purchase [events](/sdk/helium-events).
    </ResponseField>

    <ResponseField name="customPaywallTraits" type="Record<string, any>">
      (Advanced) Custom key/value pairs that you want available to the paywall.
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```tsx Expo 52+
  import { presentUpsell } from 'expo-helium';

  function YourComponent() {
    const handlePremiumPress = () => {
      presentUpsell({
        triggerName: 'premium_feature_press',
        onFallback: () => {
          // Implement logic to open a default paywall
          console.log('[Helium] onFallback called!');
        }
      });
    };

    return (
      <Button title="Try Premium" onPress={handlePremiumPress} />
    );
  }
  ```

  ```tsx Older Expo / Bare
  import { presentUpsell } from '@tryheliumai/paywall-sdk-react-native';

  function YourComponent() {
    const handlePremiumPress = () => {
      presentUpsell({
        triggerName: 'premium_feature_press',
        onFallback: () => {
          // Implement logic to open a default paywall
          console.log('[Helium] onFallback called!');
        }
      });
    };

    return (
      <Button title="Try Premium" onPress={handlePremiumPress} />
    );
  }
  ```
</CodeGroup>

You should now be able to see Helium paywalls in your app! Well done! 🎉

## Fallbacks

There are currently two ways you can handle a "fallback" situation in the rare case that a paywall fails to download or an invalid trigger is provided. You can also do both. Better to be prepared!

### 1. Use the `onFallback` function

You can pass this in when you call `presentUpsell` and handle however you want.

### 2. Provide a fallback bundle (recommended)

Provide a fallback bundle as described in [this guide](/guides/fallback-bundle).

<Tip>
  Update your fallback bundle whenever you make changes to your paywall for maximum reliability.
</Tip>

## Loading Budgets

If a paywall has not completed downloading when you attempt to present it, a loading state can show.

By default, Helium will show this loading state as needed (a shimmer view for up to 2 seconds). You can configure, turn off, or set trigger-specific loading budgets.

If the budget expires before the paywall is ready, a fallback paywall will show if available otherwise the loading state will hide and `onFallback` will be called.

```tsx
const paywallLoadingConfig: HeliumPaywallLoadingConfig = {
  useLoadingState: true,
  loadingBudget: 4,
  perTriggerLoadingConfig: {
    "onboarding": {
      loadingBudget: 5,
    },
    "trial": {
      useLoadingState: false,
    }
  },
};

initialize({
  apiKey: "helium-api-key",
  purchaseConfig: createRevenueCatPurchaseConfig(
    {apiKey: 'rc-api-key'}
  ),
  onHeliumPaywallEvent: (event: HeliumPaywallEvent) => {
    console.log('Helium Paywall Event:', event);
  },
  fallbackBundle: require('./assets/fetched.json'),
  paywallLoadingConfig: paywallLoadingConfig, // << pass to initialize
});
```

## Additional Considerations

### Expo Development Build

Please note that the Helium SDK uses native code, so you must create a [development build](https://docs.expo.dev/develop/development-builds/introduction/). A common command to run for this is:

```bash
npx expo run:ios # or npx expo run:ios --device
```

### Paywall Events

Helium emits various events during the lifecycle of a paywall. If desired, you can handle these events in the `onHeliumPaywallEvent` callback. See [Helium Events](/sdk/helium-events) for more details.

## Troubleshooting

### Check if the Helium SDK is installed

<CodeGroup>
  ```bash Expo 52+
  [ -d "node_modules/expo-helium" ] && echo "✅ expo-helium package found in node_modules" || echo "❌ expo-helium package NOT found in node_modules"
  ```

  ```bash Older Expo / Bare
  [ -d "node_modules/@tryheliumai/paywall-sdk-react-native" ] && echo "✅ paywall-sdk-react-native package found in node_modules" || echo "❌ paywall-sdk-react-native package NOT found in node_modules"
  ```
</CodeGroup>

If the package is not found, install it again:

<CodeGroup>
  ```bash Expo 52+
  npx expo install expo-helium
  ```

  ```bash Older Expo / Bare
  npm install @tryheliumai/paywall-sdk-react-native
  # or
  yarn add @tryheliumai/paywall-sdk-react-native

  # then run the following to install the native dependencies
  npx react-native link @tryheliumai/paywall-sdk-react-native
  ```
</CodeGroup>

### Check if Helium is properly installed

To verify that the Helium pod is correctly installed in your project, run:

<CodeGroup>
  ```bash Expo 52+
  grep -E "Helium" ios/Podfile.lock > /dev/null && echo "✅ Helium found in ios/Podfile.lock" || echo "❌ Helium not found in ios/Podfile.lock" && grep -E "HeliumPaywallSdk" ios/Podfile.lock > /dev/null && echo "✅ HeliumPaywallSdk found in ios/Podfile.lock" || echo "❌ HeliumPaywallSdk not found in ios/Podfile.lock"
  ```

  ```bash Older Expo / Bare
  grep -E "Helium" ios/Podfile.lock > /dev/null && echo "✅ Helium found in ios/Podfile.lock" || echo "❌ Helium not found in ios/Podfile.lock"
  ```
</CodeGroup>

If not found, try these commands:

```bash
# regenerate the ios (and android) directories
npx expo prebuild --clean

# run a development build
npx expo run:ios # or npx expo run:ios --device
```
