---
title: "Quickstart Adapty setup guide: React Native with Expo"
description: "Step-by-step guide to integrate the Adapty SDK in a React Native app built with Expo — display paywalls and process in-app purchases in under 60 minutes."
url: "https://adapty.io/blog/quickstart-adapty-setup-guide-react-native-with-expo/"
language: "en"
slug: "quickstart-adapty-setup-guide-react-native-with-expo"
category: "build-integrate"
date: "2025-06-16T12:02:37.000Z"
date_modified: "2025-09-02T16:54:25.000Z"
author: "Ben Gohlke"
---

# Quickstart Adapty setup guide: React Native with Expo

Get from zero Adapty setup to displaying a paywall and processing purchases in under 60 minutes using Expo and React Native.

## Prerequisites

- You have a working React Native app (built with Expo)
- You’re using EAS Build and Dev Client (not Expo Go)
- Your app includes at least one in-app purchase created in App Store Connect and synced in the Adapty dashboard
- You’ve created an [Adapty account](https://app.adapty.io/signup) and your app is added in the Adapty dashboard

## Project Files

To follow along with the written, video, or LLM-based tutorial, [download the project files from Github](https://github.com/adaptyteam/AdaptySDK-React-Native/tree/master/examples/FocusJournalExpo). The branch named `starter` contains the app without any Adapty integration. The `main` branch contains the finished project.

## Install the Adapty SDK

Install the Adapty SDK and required helper:

```bash
npx expo install react-native-adapty tslib
```

## Pre-build your Expo project

Because Adapty includes native code, you need to generate native iOS/Android directories:

```bash
npx expo prebuild
```

This will generate an `ios` and `android` folder, and a `Podfile`.

## Add Adapty to your iOS project

Open the `Podfile` inside your `ios/` directory and confirm Adapty was added. Then run:

```bash
cd ios
pod install
cd ..
```

You’ll also need to build a custom development client to run the app locally:

```bash
eas build --profile development --platform ios
```

Once built and installed, run the app using:

```bash
npx expo start --dev-client
```

## Activate the SDK on app launch

Create `AdaptyConstants.js` to store keys and values you’ll use throughout the project:

```bash
export default {
  API_KEY: "your-public-sdk-key",
  ACCESS_LEVEL_ID: "premium",
  PLACEMENT_ID: "on_tap_history",
};
```

Create a new file called `AdaptyService.js` in your root directory and add:

```javascript
import { adapty } from "react-native-adapty";
import AdaptyConstants from "./AdaptyConstants";

export const activationPromise = (async () => {
  try {
    await adapty.activate(AdaptyConstants.API_KEY);
  } catch (error) {
    console.error("Adapty activation failed:", error);
  }
})();
```

This exports a shared activation promise that will be awaited anywhere the SDK is used. It configures and activates the Adapty SDK. The Adapty UI library is built in, allowing you to display remotely configured paywalls with made with Paywall Builder.

## Load the user’s subscription status

It’s important to have up-to-date information on the user’s subscription status. Adapty makes this easy with something called a user profile.

In `ProfileContext.js`, modify your context to include refreshing this profile. Here’s a version showing just the additions:

```bash
// Add the following imports
import { adapty } from "react-native-adapty";
import AdaptyConstants from "../AdaptyConstants";
import { activationPromise } from "../AdaptyService";

...

export function ProfileProvider({ children }) {
    ...

  // Add this useEffect() to get a fresh profile from Adapty
  useEffect(() => {
    let mounted = true;

    (async () => {
      try {
        await activationPromise;

        const profile = await adapty.getProfile();

        if (profile && profile.accessLevels) {
          const level = profile.accessLevels[AdaptyConstants.ACCESS_LEVEL_ID];
          if (mounted) {
            setIsPremium(
              !!(level?.isActive || level?.isInGracePeriod || level?.isLifetime)
            );
          }
        }
      } catch (error) {
        console.error("Error fetching Adapty profile:", error);
      }
    })();

    return () => {
      mounted = false;
    };
  }, []);

  ...
}
```

This code fetches an updated `AdaptyProfile` object from the SDK with the latest subscription and access level information. It then checks the access level and updates `isPremium` accordingly. You can use this within the app to determine whether to show premium content or functionality.

## Show the Paywall

The next step is to present a paywall from Paywall Builder where applicable. In `HomeScreen.js`, when the user taps “View History”, we want to either navigate to the history screen (if they’re premium) or show a paywall using Adapty.

In the Home Screen, add these imports:

```bash
// Add these imports
import { adapty } from "react-native-adapty";
import { createPaywallView } from "react-native-adapty/dist/ui";
import AdaptyConstants from "../AdaptyConstants";
import { activationPromise } from "../AdaptyService";
```

Before we can show the Paywall Builder paywall, we need to fetch it from Adapty’s servers. We’ll do that and then create and present a paywall view based on that configuration.

```bash
export default function HomeScreen({ navigation }) {
    ...

    // Modify handleViewHistory to look like this:
  async function handleViewHistory() {
    await activationPromise;
    if (isPremium) {
      navigation.navigate("History");
    } else {
      try {
        const paywall = await adapty.getPaywall(
          AdaptyConstants.PLACEMENT_ID,
          "en"
        );
        if (paywall.hasViewConfiguration) {
          const view = await createPaywallView(paywall);
          view.registerEventHandlers({
            onCloseButtonPress() {
              return true;
            },
            onPurchaseCompleted(purchaseResult, product) {
              if (purchaseResult.type === "success") {
                purchasePremium(purchaseResult.profile);
                navigation.navigate("History");
                return true;
              }
              return false;
            },
            onPurchaseFailed(error) {
              console.error("Purchase failed:", error);
              return false;
            },
            onRestoreCompleted(restoredProfile) {
              purchasePremium(restoredProfile);
              navigation.navigate("History");
              return true;
            },
            onRestoreFailed(error) {
              console.error("Restore failed:", error);
              return false;
            },
            onRenderingFailed(error) {
              console.error("Error rendering paywall:", error);
              return false;
            },
          });
          await view.present();
        }
      } catch (error) {
        console.error("Error loading paywall:", error);
      }
    }
  }

  ...
}
```

The above block contains everything we need to display and manage the purchase process for a paywall. The various closures are used when success and failure states are triggered. They are your opportunity to take over control of the user experience and respond according to the outcome. The most common outcome is `onPurchaseCompleted` . In that case, we update the profile in our profile manager, and unlock premium content.

### Congrats!

At this point, your app:

- Initializes the Adapty SDK on launch
- Loads a paywall dynamically from the Adapty dashboard
- Supports purchasing and restoring subscriptions
- Unlocks premium content using `isPremium` from your context
