Back to Tutorials

Add RevenueCat to Your Expo App

Set up subscriptions and in-app purchases in an Expo React Native app with RevenueCat products, offerings, entitlements, paywalls, restore purchases, and sandbox testing.

May 30, 2026 14 min read Paddy B
Expo React Native RevenueCat Subscriptions Paywalls

The short version

Install RevenueCat's Expo-compatible SDKs, use a development build for real purchases, configure products and entitlements in the RevenueCat dashboard, then check the entitlement status in your app before unlocking paid features.

RevenueCat saves you from building your own receipt validation and subscription state system. Apple and Google still own the actual purchase flow, but RevenueCat becomes the source of truth for whether a user has access.

1. Know what you need first

Before you write code, make sure the app can actually sell a product. You need:

  • An Expo app with a stable bundle identifier and Android package name.
  • An Apple Developer account for iOS purchases and a Google Play Console account for Android purchases.
  • Subscription or in-app purchase products created in App Store Connect and Google Play Console.
  • A RevenueCat project with iOS and Android apps connected.
  • A development build or store build for real purchase testing.

Expo Go is useful while you build screens, but real native store purchases require custom native code. Use a development build when you need to test actual purchase, restore, and receipt flows.

2. Create products in the stores

RevenueCat does not replace App Store Connect or Google Play Console. Create the purchase products there first, then mirror those product identifiers inside RevenueCat.

For iOS

  • Create your app in App Store Connect with the same bundle identifier as your Expo app.
  • Create an auto-renewable subscription or consumable/non-consumable in-app purchase.
  • Fill in metadata, pricing, subscription group, review screenshot, and localization.
  • Copy the product identifier. You will use the exact same string in RevenueCat.

For Android

  • Create your app in Google Play Console with the same package name as your Expo app.
  • Create a subscription or in-app product.
  • Set the base plan, offer details, pricing, and tester access.
  • Copy the product ID for the RevenueCat dashboard.

Keep IDs boring

Use stable product IDs like pro_monthly, pro_yearly, or lifetime_unlock. Changing product IDs after release is painful because store products become part of your billing contract.

3. Set up RevenueCat

In the RevenueCat dashboard, create a project and add apps for the platforms you support. Each app gets its own public SDK API key.

  1. Add your iOS app with the App Store bundle identifier.
  2. Add your Android app with the Play Store package name.
  3. Connect App Store Connect and Google Play credentials so RevenueCat can validate receipts and sync products.
  4. Create products in RevenueCat using the exact product identifiers from the stores.
  5. Create an entitlement, usually something like pro, premium, or plus.
  6. Create an offering and attach packages such as monthly and yearly plans.

Your app should check entitlements, not individual product IDs. That way you can change pricing, package layout, trials, and offers without rewriting the feature-gating logic.

4. Install the SDKs

Install the core RevenueCat SDK and the optional UI package for RevenueCat paywalls and customer center components.

npx expo install expo-dev-client npx expo install react-native-purchases react-native-purchases-ui

After installing native modules, restart Metro and rebuild the native app. A hot reload is not enough because the native RevenueCat modules must be included in the app binary.

npx expo start --clear

5. Create a development build

If you already use EAS Build, create a development build for iOS and Android. If not, initialize EAS first.

npx eas-cli@latest login npx eas-cli@latest build:configure

A typical development build command looks like this:

npx eas-cli@latest build --profile development --platform ios npx eas-cli@latest build --profile development --platform android

Then run the app against your custom development client:

npx expo start --dev-client

RevenueCat now includes an Expo Go preview mode so your subscription screens can avoid crashing while prototyping, but purchase operations such as buying and restoring should be tested in a development build or store-distributed build.

6. Add public RevenueCat keys

RevenueCat SDK keys are public client keys, not server secrets. Store them as Expo public variables so each build profile can use the right project or environment.

# .env EXPO_PUBLIC_REVENUECAT_IOS_KEY=appl_... EXPO_PUBLIC_REVENUECAT_ANDROID_KEY=goog_...

Do not put App Store Connect private keys, Google service account JSON, webhooks secrets, or backend credentials in the Expo app. Those belong in RevenueCat or your backend.

7. Configure RevenueCat in the app

Configure Purchases once when the app starts. Pick the API key based on the platform and identify the user after login if your app has accounts.

import { Platform } from "react-native"; import Purchases, { LOG_LEVEL } from "react-native-purchases"; const revenueCatApiKey = Platform.select({ ios: process.env.EXPO_PUBLIC_REVENUECAT_IOS_KEY, android: process.env.EXPO_PUBLIC_REVENUECAT_ANDROID_KEY, }); export function configureRevenueCat(appUserId?: string) { if (!revenueCatApiKey) { throw new Error("Missing RevenueCat API key for this platform."); } Purchases.setLogLevel(__DEV__ ? LOG_LEVEL.DEBUG : LOG_LEVEL.WARN); Purchases.configure({ apiKey: revenueCatApiKey, appUserID: appUserId, }); }

Call this from your root layout, app bootstrap, or auth provider after you know the current user.

import { useEffect } from "react"; import { configureRevenueCat } from "./lib/revenuecat"; export default function App() { useEffect(() => { configureRevenueCat(); }, []); return null; }

If your app supports anonymous users first and accounts later, call Purchases.logIn(userId) after sign-in so RevenueCat can associate purchases with the account.

8. Check entitlements

The most important runtime check is the customer's entitlement state. This is what gates your premium feature.

import Purchases from "react-native-purchases"; export async function hasProAccess() { const customerInfo = await Purchases.getCustomerInfo(); return Boolean(customerInfo.entitlements.active.pro); }

Replace pro with the entitlement identifier you created in RevenueCat. Use that same entitlement across monthly, yearly, lifetime, and promotional products if they all unlock the same feature set.

9. Show a paywall

The quickest route is RevenueCat's UI package. It can present a paywall configured from the dashboard and return when the purchase flow completes.

import RevenueCatUI, { PAYWALL_RESULT } from "react-native-purchases-ui"; export async function presentPaywall() { const result = await RevenueCatUI.presentPaywall({ requiredEntitlementIdentifier: "pro", }); return result === PAYWALL_RESULT.PURCHASED || result === PAYWALL_RESULT.RESTORED; }

If you want a custom screen, fetch offerings and purchase a package yourself.

import Purchases from "react-native-purchases"; export async function purchaseCurrentMonthlyPackage() { const offerings = await Purchases.getOfferings(); const monthly = offerings.current?.monthly; if (!monthly) { throw new Error("Monthly package is not configured."); } const { customerInfo } = await Purchases.purchasePackage(monthly); return Boolean(customerInfo.entitlements.active.pro); }

For most apps, start with the hosted paywall. Move to a custom paywall when your design, analytics, or upgrade flow needs more control.

10. Restore purchases

Apple requires a visible restore purchases option for apps with non-consumable purchases or subscriptions. Keep it simple.

import Purchases from "react-native-purchases"; export async function restorePurchases() { const customerInfo = await Purchases.restorePurchases(); return Boolean(customerInfo.entitlements.active.pro); }

Show a clear success message when access is restored and a neutral message when no active purchase is found.

11. Test the full flow

Test purchases on real devices using Apple sandbox and Google Play license testers. Simulators are useful for layout, but do not treat simulator purchase behavior as the final answer.

  • Install a development build or TestFlight/internal testing build.
  • Confirm Purchases.configure logs the correct platform key.
  • Open the paywall and confirm offerings load from RevenueCat.
  • Complete a sandbox purchase.
  • Close and reopen the app, then confirm the entitlement remains active.
  • Delete and reinstall the app, then confirm restore purchases works.
  • Check the customer page in RevenueCat to verify the purchase and entitlement state.

If offerings do not load

Check the bundle ID or package name, product IDs, store product status, RevenueCat app API key, offering identifier, and whether the app build is signed for the same store app where the products exist.

12. Production checklist

  • Gate paid features with RevenueCat entitlements, not a local boolean.
  • Keep a restore button in account settings or the paywall.
  • Use stable app user IDs if users have accounts.
  • Set up RevenueCat webhooks if your backend needs to react to renewals, cancellations, or refunds.
  • Test fresh install, upgrade, downgrade, restore, expiration, and account login flows.
  • Make the app usable when the network is slow or RevenueCat is temporarily unavailable.

The main pattern is simple: stores own payments, RevenueCat owns subscription truth, and your Expo app unlocks features by reading entitlements.

Support tutorials