How to Set Up RevenueCat in a React Native (Expo) App
By Arslan Shaukat
In-app purchases are where a lot of indie apps stall. The store dashboards are confusing, receipt validation is genuinely hard to get right, and a single mistake means either lost revenue or angry users who paid and got nothing. RevenueCat exists to take that whole layer off your plate. Here's a clean path to wiring it into an Expo app.
1. Install the SDK
npx expo install react-native-purchasesRevenueCat needs native modules, so you'll run this in a development build or a prebuilt app — it won't work in Expo Go. If you're on the managed workflow, create a dev build with npx expo run:ios (or run:android).
2. Configure on launch
Initialize the SDK once, as early as possible — typically in your root layout or an auth provider:
import Purchases from "react-native-purchases";
Purchases.configure({
apiKey: Platform.select({
ios: process.env.EXPO_PUBLIC_RC_IOS_KEY!,
android: process.env.EXPO_PUBLIC_RC_ANDROID_KEY!,
})!,
});Keep the keys in environment variables. They're public client keys, but centralizing them keeps your config in one place.
3. Fetch offerings
An offering is the set of products you present on your paywall. RevenueCat lets you change which products are shown from its dashboard — no app update required:
const offerings = await Purchases.getOfferings();
const current = offerings.current;
const monthly = current?.availablePackages.find(
(p) => p.packageType === "MONTHLY"
);4. Make a purchase
try {
const { customerInfo } = await Purchases.purchasePackage(monthly!);
const isPro = customerInfo.entitlements.active["pro"] !== undefined;
// unlock the app
} catch (e: any) {
if (!e.userCancelled) {
// surface a real error to the user
}
}Notice you check an entitlement ("pro"), not a specific product. That's the trick that keeps your code stable: you can add, rename, or A/B test products without touching the unlock logic.
5. Gate features on entitlements
Read the customer's entitlements anywhere you need to gate a feature:
const info = await Purchases.getCustomerInfo();
const isPro = info.entitlements.active["pro"] !== undefined;Store that in a context or store so your UI reacts to it, and always offer a Restore Purchases button (Purchases.restorePurchases()) — Apple will reject apps that don't.
Gotchas worth knowing
- Test on a real build. Sandbox purchases behave differently from Expo Go (which can't do them at all).
- Entitlements over products. Always unlock on entitlement identifiers, never on raw product IDs.
- Handle `userCancelled`. A cancelled purchase is not an error — don't show a scary alert.
- Restore is mandatory. Missing it is a common App Store rejection.
That's the whole loop: configure, fetch offerings, purchase, check entitlements. If you'd like this already scaffolded — paywall screen, entitlement gating, and restore flow included — my React Native Starter ships with it wired in so you can skip straight to designing the offer.
Skip the boilerplate
Production-ready React Native starters and UI kits — buy once, clone, and start on the feature that matters.
Browse the templatesMore