DEV Community

albert nahas
albert nahas

Posted on • Originally published at leandine.hashnode.dev

Implementing Subscriptions in Expo with RevenueCat

Implementing subscriptions in mobile apps can seem overwhelming, especially when you’re building with Expo and React Native. Managing in-app purchases, handling entitlement logic, and providing a seamless paywall experience all require careful planning and robust tooling. Fortunately, RevenueCat has emerged as a popular solution to simplify these challenges, offering a cross-platform abstraction over App Store and Google Play billing systems—including StoreKit 2 support for iOS.

Let’s explore how to implement subscriptions in Expo apps using RevenueCat, cover key concepts like entitlements, and review common paywall patterns. By the end, you’ll have a practical roadmap for adding robust, scalable in-app subscriptions to your React Native project.

Why RevenueCat for Expo Subscriptions?

The React Native and Expo ecosystem offers several options for in-app purchases, but RevenueCat stands out for several reasons:

  • Unified API: One codebase for both iOS and Android, abstracting away platform quirks.
  • Entitlement Management: Centralizes subscription status and logic (cancellations, renewals, grace periods).
  • StoreKit 2 Support: Embraces Apple’s latest in-app purchase APIs for better reliability and security.
  • Analytics & Webhooks: Out-of-the-box analytics and integrations with your backend or third-party tools.

If you’ve ever used lower-level libraries like react-native-iap, you know how much work goes into handling edge cases and platform differences. With RevenueCat, you focus on your business logic and user experience, not the minutiae of billing APIs.

Prerequisites

To follow along, you’ll need:

  • A React Native project (Expo SDK 49+ recommended for EAS support)
  • A RevenueCat account and project (sign up here)
  • App Store Connect and Google Play Console access for configuring products

Note: Expo Go does not support native in-app purchase modules. To use RevenueCat with Expo, you must build your app with EAS Build (expo build is deprecated), or use a custom development client via Expo Dev Client.

Setting Up RevenueCat in Expo

RevenueCat provides the react-native-purchases SDK for integration. Here’s how to add it to your Expo project.

1. Install the Purchases SDK

If you’re using Expo managed workflow with EAS, install the library:

npx expo install react-native-purchases
Enter fullscreen mode Exit fullscreen mode

Then add the plugin to your app.json or app.config.js:

{
  "expo": {
    "plugins": [
      [
        "react-native-purchases",
        {
          "ios": {
            "userTrackingUsageDescription": "Your data will be used for in-app purchases."
          }
        }
      ]
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Configure Products in RevenueCat

  • In App Store Connect / Google Play Console: Create your subscriptions and note their product IDs.
  • In RevenueCat Dashboard: Add these products to your project, group them into an “offering,” and define entitlements (such as premium_access).

3. Initialize Purchases in Your App

You’ll need to initialize the SDK with your RevenueCat API key (found in the dashboard):

import Purchases from 'react-native-purchases';
import Constants from 'expo-constants';

const REVENUECAT_API_KEY = Constants.manifest?.extra?.REVENUECAT_API_KEY;

useEffect(() => {
  Purchases.configure({ apiKey: REVENUECAT_API_KEY });
}, []);
Enter fullscreen mode Exit fullscreen mode

For production, you may want to set a unique user ID for better tracking:

Purchases.configure({ apiKey: REVENUECAT_API_KEY, appUserID: user.id });
Enter fullscreen mode Exit fullscreen mode

Fetching Offerings and Products

RevenueCat’s “offerings” represent the products you want to show on your paywall. Fetching them is straightforward:

const [packages, setPackages] = useState([]);

useEffect(() => {
  async function loadOfferings() {
    const offerings = await Purchases.getOfferings();
    if (offerings.current) {
      setPackages(offerings.current.availablePackages);
    }
  }
  loadOfferings();
}, []);
Enter fullscreen mode Exit fullscreen mode

You can now display these packages in your paywall UI, showing localized pricing and descriptions.

Handling Purchases and Entitlements

Purchasing a subscription is as simple as calling Purchases.purchasePackage. RevenueCat handles the underlying StoreKit 2 (on iOS 15+) or Google Play Billing APIs.

async function subscribe(pkg) {
  try {
    const { customerInfo } = await Purchases.purchasePackage(pkg);

    if (customerInfo.entitlements.active['premium_access']) {
      // Unlock premium content
    }
  } catch (e: any) {
    if (!e.userCancelled) {
      // Handle error (e.g., display message)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Checking Subscription Status

To gate features or content, check if the user’s entitlement is active:

useEffect(() => {
  async function checkEntitlement() {
    const { entitlements } = await Purchases.getCustomerInfo();
    setIsPremium(!!entitlements.active['premium_access']);
  }
  checkEntitlement();
}, []);
Enter fullscreen mode Exit fullscreen mode

This pattern ensures your app always reflects the user’s latest subscription state—even if they change it outside your app.

Supporting StoreKit 2

With iOS 15+, Apple introduced StoreKit 2, a modern API for in-app purchases. RevenueCat’s SDK (version 5+) automatically uses StoreKit 2 when available, providing benefits like:

  • Improved reliability for transaction updates
  • Better handling of refunds, family sharing, and introductory offers
  • Enhanced security and user privacy

There’s nothing special required on your end; RevenueCat abstracts away these details, so your Expo app stays future-proof.

Paywall Patterns and Best Practices

Designing a high-converting paywall is as important as the purchase logic itself. Here are some proven patterns:

1. Soft vs. Hard Paywalls

  • Soft Paywall: Gently prompt users to upgrade after experiencing free content or features.
  • Hard Paywall: Block access to key features until a subscription is purchased.

For most apps, a soft paywall with a clear value proposition converts better and feels less aggressive.

2. Clear Value Communication

Showcase the benefits of your premium offering: what does the user unlock? Use icons, feature lists, and testimonials.

3. Transparent Pricing

Display local prices, trial durations, and billing terms fetched from RevenueCat’s product data. Avoid hardcoding prices.

packages.map(pkg => (
  <View key={pkg.identifier}>
    <Text>{pkg.product.title}</Text>
    <Text>{pkg.product.description}</Text>
    <Text>{pkg.product.priceString}</Text>
    <Button title="Upgrade" onPress={() => subscribe(pkg)} />
  </View>
));
Enter fullscreen mode Exit fullscreen mode

4. Restore Purchases

Always provide a way for users to restore purchases, especially for iOS users:

async function restore() {
  const { entitlements } = await Purchases.restorePurchases();
  if (entitlements.active['premium_access']) {
    // Unlock premium content
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Handling Cancellations and Grace Periods

RevenueCat keeps your app updated with changes (cancellations, renewals, billing issues) via the getCustomerInfo call. For more advanced workflows (e.g., notifying users of expirations), consider using RevenueCat’s webhooks or integrating with tools like Firebase, Segment, or Recallix for downstream actions.

Testing In-App Purchases

  • iOS: Use TestFlight builds and sandbox accounts. RevenueCat provides a sandbox environment.
  • Android: Upload internal test builds to Google Play Console.

Make sure to test scenarios like subscription upgrades, downgrades, cancellations, and network interruptions.

Troubleshooting and Tips

  • Expo Go Limitation: You cannot test purchases in Expo Go; use a custom dev client or EAS build.
  • App Store Review: Always provide a way for reviewers to access premium features (e.g., via a test account or special entitlement).
  • Backend Verification: RevenueCat can notify your server of purchase events via webhooks, enabling advanced analytics or feature unlocks.

Alternatives to RevenueCat

While RevenueCat is popular for Expo subscriptions, other solutions exist:

  • react-native-iap: Lower-level library for direct integration with StoreKit 2 and Google Play Billing. More manual work, but total control.
  • Qonversion, Glassfy: Similar SaaS platforms offering in-app purchase management.
  • Native modules: For bare React Native or custom native code, direct StoreKit 2 integration is possible, but not recommended for most Expo workflows.

Key Takeaways

Implementing subscriptions in Expo with RevenueCat streamlines the process of managing in-app purchases, entitlements, and paywall logic across iOS and Android. By leveraging RevenueCat’s unified API and StoreKit 2 support, you avoid platform-specific headaches and can focus on delivering value to your users. Remember to design a user-centric paywall, keep your pricing transparent, and always test thoroughly across both platforms.

With these tools and patterns, your React Native app will be ready to offer subscriptions users trust—and revenue you can count on.

Top comments (0)