# How to Implement Subscriptions in Expo Router

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-07. 5 min read.
> Source: https://vp0.com/blogs/how-to-implement-subscriptions-in-expo-router-ai

Showing a paywall screen is trivial. Knowing whether the user is entitled, on every launch, is the whole problem.

**TL;DR.** Subscriptions in an Expo Router app are an entitlement problem, not a routing one: the paywall screen is trivial, but knowing whether a user is currently entitled and what that unlocks, correctly on every launch, is the whole task. Apple owns the billing (digital subscriptions go through StoreKit), so the app presents, completes, verifies, and gates. Use RevenueCat (react-native-purchases, 634,304 weekly downloads) for the edge cases most apps hit, or StoreKit 2 direct if you want to own it, and verify entitlement server-side, never the client. Wire it into an entitlement context route guards read, resolve it before gating (no locked-then-unlocked flash), and treat restore, grace states, and required terms as build requirements. A free VP0 design supplies the paywall and gated screens.

## What is actually hard about subscriptions in an Expo Router app?

Not the routing, the entitlement. [Expo Router](https://docs.expo.dev/router/introduction/) gives you file-based navigation, so showing a paywall screen or a premium tab is trivial; the hard part is the question every subscription app must answer correctly on every launch: **is this user currently entitled, and which screens does that unlock?** Get entitlement wrong and you either lock out a paying customer or give away premium for free, both of which are worse than any navigation bug. So a subscriptions implementation is really an entitlement-state problem that happens to have some screens attached.

The honest framing first: **Apple owns the billing**, full stop. In-app purchases for digital content go through StoreKit and Apple takes its cut, and no implementation changes that, so the app's job is presenting the offer, completing the purchase through Apple, verifying it, and gating access, not inventing a payment flow. An Expo Router subscription is StoreKit (or a layer over it) wired into your route guards.

## RevenueCat or StoreKit directly?

The decision most teams face, and both are legitimate:

| Approach | What you get | Best for |
| --- | --- | --- |
| [RevenueCat](https://www.revenuecat.com/docs/) (react-native-purchases) | Entitlements, receipt validation, cross-platform, analytics | Most apps; less to build and maintain |
| [StoreKit](https://developer.apple.com/documentation/storekit) direct (StoreKit 2) | No dependency, full control | Teams who want to own it and can |

RevenueCat (its react-native-purchases SDK pulls 634,304 weekly downloads) has become the default because subscriptions are deceptively full of edge cases, renewals, grace periods, billing retries, refunds, family sharing, cross-platform entitlement, and it handles the server-side receipt validation and entitlement bookkeeping you would otherwise build and maintain yourself. StoreKit 2 direct is a fine choice for a team that wants no dependency and can own the receipt-verification and entitlement logic. Either way, the load-bearing rule is the same: **entitlement is verified server-side**, never trusted from the client, the same client-says-so-is-not-enough discipline as any [paywall or IAP build](/blogs/native-iap-swiftui-without-revenuecat/).

## How does entitlement wire into Expo Router?

As a state that route guards read, computed from the verified subscription. The pattern: an entitlement context (is the user pro?) initialized at launch from the SDK's verified state, and premium routes that check it, redirecting to the paywall when access is missing. The honesty details that matter:

- **Resolve entitlement before gating**: on launch, the app must know the real subscription state before deciding what to show, so premium screens wait for entitlement to resolve rather than flashing locked-then-unlocked (or worse, the reverse).
- **Restore purchases is mandatory**: a returning user on a new device must be able to restore, and App Review checks for it, so a visible restore action is not optional.
- **Pending and grace states are real**: a purchase processing, a renewal in billing retry, a subscription in grace period, each is a state the gating must handle, never a binary pro/not-pro that locks a user out during a billing hiccup.

The paywall itself is a route, and where it appears in the flow is a product decision (after the value moment usually beats on launch), the same placement honesty as any [paywall design](/blogs/high-converting-ios-paywall-template-react-native/).

## What completes a subscriptions implementation?

The compliance and the honesty. App Review requires the subscription terms, price, and a link to terms/privacy on the paywall, plus working restore, and an account-deletion path that does not strand a subscription, so these are build requirements, not polish. And the honest framing throughout: the app presents and gates, Apple bills and the server verifies, so success shows only on verified purchase (never an optimistic unlock when the StoreKit sheet returns), the same pending-not-optimistic rule as every payment flow.

The screens, the paywall, the premium-gated tabs, the manage-subscription and restore screens, come as a free [VP0](https://vp0.com) design, so an agent wires RevenueCat or StoreKit into Expo Router's route guards on a UI already shaped for entitlement-gated navigation rather than a paywall bolted on after.

## Key takeaways: subscriptions in Expo Router

- **The hard part is entitlement, not routing**: get "is this user entitled and what does it unlock" right on every launch; the screens are easy.
- **Apple owns the billing**: digital subscriptions go through StoreKit; the app presents, completes, verifies, and gates, it does not invent payment.
- **RevenueCat or StoreKit 2 direct**: RevenueCat handles the edge cases for most apps; StoreKit direct suits teams who want to own it.
- **Verify entitlement server-side and resolve it before gating**: never trust the client, and never flash locked-then-unlocked on launch.
- **Restore, pending/grace states, and the required terms are build requirements**: not optional, and App Review checks for them.

## Frequently asked questions

**How do I implement subscriptions in an Expo Router app?** Treat it as an entitlement problem: wire RevenueCat (react-native-purchases) or StoreKit 2 into an entitlement context initialized at launch from verified state, gate premium routes on it, and show a paywall route when access is missing. Verify entitlement server-side, include restore, and handle pending and grace states. A free VP0 design supplies the paywall and gated screens.

**Should I use RevenueCat or StoreKit directly for subscriptions?** RevenueCat for most apps: its react-native-purchases SDK handles the many subscription edge cases (renewals, grace periods, billing retries, cross-platform entitlement, receipt validation) you would otherwise build and maintain. StoreKit 2 direct is a fine choice for teams who want no dependency and can own the receipt-verification and entitlement logic themselves.

**Where does entitlement checking belong?** In a state that route guards read, computed from server-verified subscription status, never trusted from the client. Resolve the real entitlement at launch before deciding what to show, so premium screens do not flash locked-then-unlocked, and gate routes on that resolved state rather than a client-side flag a user could spoof.

**Is restore purchases required?** Yes: a returning user on a new device must be able to restore their subscription, and App Review explicitly checks for a working restore action, so it is a build requirement, not optional polish. Pair it with handling pending and grace-period states so a billing hiccup never locks out a paying user.

**Does the app handle the subscription billing itself?** No: Apple owns billing for digital subscriptions through StoreKit and takes its cut, so the app presents the offer, completes the purchase through Apple, verifies the receipt server-side, and gates access. Showing premium as unlocked when the StoreKit sheet returns, before verification, is the optimistic-state bug to avoid.

## Frequently asked questions

### How do I implement subscriptions in an Expo Router app?

Treat it as an entitlement problem: wire RevenueCat (react-native-purchases) or StoreKit 2 into an entitlement context initialized at launch from verified state, gate premium routes on it, and show a paywall route when access is missing. Verify entitlement server-side, include restore, and handle pending and grace states. A free VP0 design supplies the paywall and gated screens.

### Should I use RevenueCat or StoreKit directly for subscriptions?

RevenueCat for most apps: its react-native-purchases SDK handles the many subscription edge cases (renewals, grace periods, billing retries, cross-platform entitlement, receipt validation) you would otherwise build and maintain. StoreKit 2 direct is fine for teams who want no dependency and can own the receipt-verification and entitlement logic themselves.

### Where does entitlement checking belong?

In a state that route guards read, computed from server-verified subscription status, never trusted from the client. Resolve the real entitlement at launch before deciding what to show, so premium screens do not flash locked-then-unlocked, and gate routes on that resolved state rather than a client-side flag a user could spoof.

### Is restore purchases required for subscriptions?

Yes: a returning user on a new device must be able to restore their subscription, and App Review explicitly checks for a working restore action, so it is a build requirement, not optional polish. Pair it with handling pending and grace-period states so a billing hiccup never locks out a paying user.

### Does the app handle the subscription billing itself?

No: Apple owns billing for digital subscriptions through StoreKit and takes its cut, so the app presents the offer, completes the purchase through Apple, verifies the receipt server-side, and gates access. Showing premium as unlocked when the StoreKit sheet returns, before verification, is the optimistic-state bug to avoid.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
