12 Nov 2026 · 6 min read

Best paywall tools for Expo in 2026

RevenueCat, Adapty and Stripe Checkout for React Native and Expo: Expo Go support, store rules, pricing and when to pick each.

An Expo app that sells subscriptions has three real options for the paywall layer: RevenueCat, Adapty, and Stripe Checkout. RevenueCat and Adapty wrap StoreKit and Google Play Billing in a native SDK. Stripe Checkout opens a page that Stripe hosts in the system browser. For most apps, pick RevenueCat. Pick Adapty for a no-code paywall builder and A/B tests on placements. Pick Stripe when you sell on the web too, or when what you sell is not digital goods that the stores require in-app purchase for.

The three share one API. Screens call payments.useEntitlements() and payments.presentPaywall?.(), so the code that gates a feature does not change when you switch. What does change is how the money moves, what Expo Go can run, and where the server code lives.

Two store paths and one hosted pageA shared @/lib/payments layer sits above RevenueCat, Adapty and Stripe Checkout. RevenueCat and Adapty buy through Apple StoreKit or Google Play Billing. Stripe Checkout is a page hosted by Stripe, outside store billing.@/lib/paymentsone API for all threeRevenueCatnative SDKAdaptynative SDKStripe Checkouthosted page in browserApple StoreKit / Google Play Billingin-app purchase, store rules applyStripenot store billingDigital goods sold inside the app fall under the store rules, whichever option you pick.

How the purchase happens

RevenueCat and Adapty sell through the stores. When the user taps Buy, StoreKit or Play Billing charges them, and the SDK reports the result. Stripe Checkout sells through a page that Stripe hosts, reached from the app by a redirect. Store billing is not involved.

That difference is what the stores care about. Apple's App Review Guideline 3.1.1 says that if you unlock features or functionality in your app, such as with subscriptions, you must use in-app purchase. Outside the US storefront, it also bars buttons and links to other purchase methods unless an Apple entitlement permits them. Google Play's Payments policy says that Google Play's billing system is required for in-app purchases of digital goods and services. Both policies have region-specific programs, and both change. Read the current text before you ship a Stripe link inside an app.

The comparison in one table

RevenueCatAdaptyStripe Checkout
Store billing (StoreKit, Play Billing)YesYesNo, hosted page in the browser
Runs in Expo GoNoNo, mock mode onlyYes
Native build neededYesYesNo
PaywallDashboard paywall, or /paywallPaywall Builder, or /paywall/paywall modal
Change the paywall without an app updateYes, dashboard paywallsYes, flows and paywallsPrices in Stripe; the screen is code
Server code in your treeNoNoYes: four API routes
Entitlementpropremium (access level)pro (STRIPE_ENTITLEMENT)
Price, as of 12 Nov 2026Free to $2,500 monthly tracked revenue, then 1%Free under $5K a month, then 1% of monthly tracked revenueBilling 0.7% of billing volume; card fees separate

RevenueCat: the default

RevenueCat wraps StoreKit and Google Play Billing behind one SDK and a unified subscription backend, and it can run the paywall from its dashboard. Its docs say that paywalls are configured remotely, without code changes or app updates. Run the setup, then add the two public SDK keys to .env:

bun run setup --payments revenuecat

EXPO_PUBLIC_REVENUECAT_IOS_KEY takes the App Store key (appl_…), and EXPO_PUBLIC_REVENUECAT_ANDROID_KEY takes the Play Store key (goog_…). Create the subscriptions in App Store Connect and Play Console, add them to RevenueCat under Products, attach them to an entitlement named pro, and mark an offering as current. A product that exists only in RevenueCat never shows a price.

Then gate a feature on the entitlement:

import { payments } from "@/lib/payments";

const { active, loading } = payments.useEntitlements(); // active: ["pro"]
if (!loading && !active.includes("pro")) await payments.presentPaywall?.();

presentPaywall() shows the dashboard paywall when you have designed one, and falls back to ReadyNative's /paywall route when you have not. Expo's own in-app purchases guide makes the same point: purchase libraries need custom native code, which Expo Go does not configure. The App Review steps are in RevenueCat subscriptions in an Expo Router app.

Adapty: the no-code paywall builder

Adapty uses the same store billing underneath. Its selling point is the Paywall Builder: you design a flow or paywall visually, attach it to a placement, and test variants with A/B tests. Adapty's SDK docs describe the builder as a no-code interface that renders the UI and handles the purchase for you, and its paywalls change without an app release.

bun run setup --payments adapty

The one public key, EXPO_PUBLIC_ADAPTY_PUBLIC_KEY, serves both platforms. The placement id defaults to default; set EXPO_PUBLIC_ADAPTY_PLACEMENT_ID if yours has another name. The module also adds expo-build-properties with useFrameworks set to dynamic, which the SDK's Swift Package dependencies need. In Expo Go, the SDK falls back to mock mode: no products and no purchases.

Stripe Checkout: web sales and Expo Go

Stripe is the odd one out. ReadyNative's module has no native payments SDK. The app opens Stripe's hosted Checkout in the system browser, and your own API routes create the session, read entitlements and receive webhooks. Stripe describes Checkout as a fully-featured payment page, either embedded on your site or reached by redirect to a Stripe-hosted page. Because the purchase happens in a browser, Expo Go runs it.

bun run setup --payments stripe --backend api-routes
stripe listen --forward-to localhost:8081/api/stripe/webhook

The module needs the API routes backend, because it sets the web output to server. Keep STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET out of any EXPO_PUBLIC_ variable. The routes charge the user that the server verifies from the session, not the customer id the app sends. The repo's examples/snap-recipe runs Checkout next to RevenueCat with one merged entitlement set.

Stripe fits physical goods and services, and web sales. Apple's guideline 3.1.3(e) says that physical goods and services consumed outside the app must use other purchase methods, such as Apple Pay or traditional credit card entry. Digital unlocks are different: guideline 3.1.1 applies, so check a Stripe-only subscription against the current store rules before you ship it.

What it costs, as of 12 Nov 2026

Prices change, so these figures come from each vendor's pricing page, fetched on 12 Nov 2026. RevenueCat's Pro plan costs nothing up to $2,500 in monthly tracked revenue, then 1% of what you track. Adapty is free while you earn under $5K a month, then pays 1% of monthly tracked revenue. Stripe's pricing page lists Billing, for recurring subscriptions, at 0.7% of billing volume, and prices card payments separately. Check the pages again before you commit.

When each one wins

Read next: RevenueCat subscriptions in an Expo Router app, for the dashboard and App Review steps, and Expo Go vs a development build, for why two of these need a build. The option pages are in the payments module docs.

FAQ

What are the best paywall tools for React Native and Expo?

For most Expo apps, pick RevenueCat: it wraps StoreKit and Play Billing, and its paywall can be changed from the dashboard. Adapty suits a no-code Paywall Builder. Stripe Checkout suits web sales and runs in Expo Go.

Is RevenueCat or Adapty better for Expo?

Neither wins for every app. RevenueCat is the recommended default here. Choose Adapty when its Paywall Builder and placement A/B tests matter more. Both need a development build, and both have a free tier below a revenue threshold.

Can I use Stripe for in-app subscriptions in Expo?

You can run Stripe Checkout from an Expo app, and Expo Go works with it. The stores still decide what may be sold inside the app: Apple and Google require in-app purchase for digital goods, so check their current rules first.

More guides