How to add subscriptions to a Bolt-generated Expo app
Want to add in-app subscriptions to your Bolt app? This guide shows how to build a working paywall UI using prompts only, then integrate RevenueCat to handle real purchases and entitlements on Android using EAS Build.

Summary: Bolt-generated apps previewed in Expo Go cannot support RevenueCat subscriptions due to missing native modules. To enable in-app purchases, developers must export the project, configure EAS Build, and install the RevenueCat SDK locally. Subscription products should be created in Google Play Console, linked to RevenueCat, and assigned to offerings. Subscriptions are then managed via entitlement checks, which control gated content access. Building with EAS enables full subscription functionality for production Android apps.
In this guide we are going to look at how to add in-app purchases, meaning simple monthly and yearly subscriptions, into the Expo React Native app we have created with Bolt. We are also going to add a paywall to block users from accessing part of the content in the app, and implement logic for checking whether the user is eligible to access content.
We are first going to create our project with Bolt, from which you can then start making your own changes to the apps. In this guide we are going to build an app that works on both iOS and Android, but you can of course, choose to build for only one of those platforms if you prefer. You can find the example project from this repository.
Step 1: Create your Bolt app with prompts
As the first step let’s open bolt.new and give the following prompt to get things started:
1I'm building a mobile app for browsing cat pictures. The app should have two tabs: Feed and Profile.
2
3In the Feed tab:
4- Display a vertical scrollable list of 15 cat images.
5- Only the first 2 images should be fully visible. The remaining 13 images must have a semi-transparent overlay with the text:
6"Subscribe to unlock" centered over the image.
7- Tapping on a locked image should present a RevenueCat paywall screen, built using react-native-purchases-ui.
8- Install react-native-purchases using version 8.11.4 or higher, and also install react-native-purchases-ui using version 8.11.4
9- Use the Paywall component from react-native-purchases-ui to display and handle the subscription purchase.
10- The product shown must include 1_month_subscription_ios in the offering.
11- Do not create a custom purchase button or product card UI; rely on the RevenueCat-provided Paywall component entirely.
12
13Hooks and Logic:
14- Create and export a custom hook called initializePurchases that configures the RevenueCat SDK using the actual Purchases.configure() method. Use this hook in _layout.tsx.
15- Create and export a hook called useSubscriptionStatus that checks the user's subscription status using Purchases.getCustomerInfo() and entitlements logic. Return a boolean isSubscribed value.
16- Do not mock the purchase or subscription logic; use the real RevenueCat methods.
17- Use the useSubscriptionStatus hook in both Feed and Profile tabs to show/hide locked content and reflect subscription status.
18
19In the Profile tab:
20- Show a simple message based on subscription state (e.g., "You are subscribed" / "Not subscribed") using useSubscriptionStatus.
21
22Additional Constraints:
23- Do not include installation instructions or unrelated boilerplate.
24- Avoid backend integration or persistent storage; rely on RevenueCat SDK only.
25- Keep code modular with well-named components for Feed, Profile, OverlayedImage.
26- Ensure the app runs in Expo Go—only use libraries that are compatible with it.
27- Do not implement any manual purchasing UI or fallback logic. The react-native-purchases-ui Paywall must handle the full purchase flow.
Why not just add subscriptions as part of the prompt?
This prompt should provide you with a working app that has all the UI parts and code necessary for handling subscriptions. If you navigate around the application, you’ll notice that everything appears functional, and even the subscription logic is already in place. However, when you press the subscribe button, nothing happens. This is expected—while the code is present, it won’t function inside the current preview environment.
You might have noticed that the prompt includes the react-native-purchases package. This is RevenueCat’s React Native SDK, required for enabling in-app purchases. The reason subscriptions still don’t work at this stage is because Bolt uses an app called Expo Go to preview your app. Expo Go is a prebuilt preview app that comes bundled with a fixed set of native modules. These allow for rich interactions with native code—like building advanced UIs or accessing the camera—but they don’t include the native modules required for in-app purchases.
To address this limitation, react-native-purchases automatically detects when it’s running in Expo Go. When Expo Go is detected, the SDK enters Preview API Mode. In this mode, all native calls are replaced with JavaScript-level mock APIs. While this means subscriptions won’t actually function in the preview, the integration won’t break either. Instead, you can safely navigate your app and verify that your subscription-related logic is wired up correctly, without interrupting your development flow.
At this point, you have a fully functional app created with Bolt, including subscription logic and the RevenueCat SDK. All features should work as expected—except for actual subscription flows, which require native modules not available in Expo Go. You can continue using Bolt to build and enhance your app. Once you’re happy with the result, proceed to the next section to learn how to deploy your app using Expo Application Services (EAS), which will enable full subscription functionality.
Step 2: Switch from Expo Go to EAS Build
Now that our app has the basic UI for subscriptions in place, we can integrate the actual subscription logic using the react-native-purchases SDK from RevenueCat. Since the SDK includes native code, and our app uses Expo, we need to configure it to work with EAS Build, Expo’s custom build service.
To get started download the project you have created with Bolt to your computer and open it in your IDE of your choice (e.g., VS Code, Cursor). You will also need the command line, so open that and navigate to your project folder to run the following command:
1npm install
This will install the depencies of your app.
Prepare Expo for native modules
To enable native code in an Expo project, we need to switch from Expo Go to EAS Build. This is required for react-native-purchases and any other library that depends on native modules.
Follow the official Bolt Expo integration guide to configure your app. The key steps are:
1. Install eas-cli if you haven’t already:
1npm install -g eas-cli
2. Log in and configure the project:
1eas login
2eas build:configure
3. Once configured, use eas build instead of expo start to build your project for iOS or Android:
1eas build -platform android
2// or
3eas build -platform ios
This will create a production build of your application that you can then submit to the app stores. If you run into any errors or need additional support, check out Expo’s guide for creating production builds for Android and their guide for creating production iOS builds.
We have now a working production version of our app for Android and iOS that we can start adding subscriptions into. Before we do that we need to first set up Google Play Console, App Store Connect, and RevenueCat, connect these three, and add products for our users to purchase.
Step 3: Set Up Products in apps stores and RevenueCat

Before we work on enabling subscriptions for our app, let’s set up RevenueCat and configure our products. In this tutorial we are only going to configure products for the Play Store so that we can have working Android apps at the end of these tutorials.
Start by creating a new RevenueCat account at revenuecat.com for free.
Connect RevenueCat to Google Play Store
First we need to connect our Google Play account to the RevenueCat dashboard, which will allow RevenueCat’s servers to communicate with Google Play Store on your behalf. To do this we need to provide RevenueCat with a set of service credentials. The process for configuring all of these is a bit complex, but this is all done to give RevenueCat with only the absolutely necessary access; allowing us to keep security level high.
To get access to the Google Play console, where all your distributed apps will live, you need to sign up for a Google Play developer account, which currently costs a one time fee of $25. Learn more about setting up a Play Console developer account from Google’s official documentation.
Once you’ve set up your Play Console account, follow RevenueCat’s guide to connect Google Play console and RevenueCat.
Configure Google Play with Products
With our RevenueCat account connected to the Google Play Store, we’re now ready to configure in-app subscriptions for our app. First, log into the Google Play Console, select your application, and navigate to Monetize > Products > Subscriptions in the sidebar.
Click the Create subscription button and configure your pricing and billing periods (e.g., monthly and yearly). Be sure to take note of the Product ID for each subscription—you’ll need this when connecting your app to RevenueCat.
For more detailed information follow our Google Play product setup guide.
Connect RevenueCat to App Store Connect
To set up products for iOS, iPadOS, macOS, tvOS, and watchOS, start by logging into App Store Connect. App Store Connect is Apple’s central hub for managing app releases, TestFlight, in-app purchases, and more. To do this you need an Apple Developer account, which costs $99 a year.
Once you’ve set up your App Store Connect account, follow RevenueCat’s guide on connecting App Store Connect and RevenueCat.
Configure App Store Connect with Products
Navigate to your app and in the left navigation column under monetization select the Subscriptions section. Here you can define your subscriptions for your app. You need to app a new subscription group, then create a new subscription and include the subscription in the previously created group.
For more detailed information follow our iOS product setup guide.
Import Products into RevenueCat
Now that your subscription products are live in both app stores, head back to the RevenueCat dashboard.
Navigate to your project’s Products tab and click + New > Import Products. RevenueCat will automatically fetch your available subscriptions from Google Play and App Store Connect. Select the ones you want to import and click Import. Your subscriptions will now appear in your RevenueCat project, ready to use.
Create an Offering
Offerings in RevenueCat are how you define which products to show in your app. This makes it easy to test pricing, paywalls, and product combinations—all without needing to update your app.
Go to the Offerings tab and click + New to create your first offering. You’ll assign the products (like your monthly and yearly subscriptions) to this offering, which your app will reference when displaying a paywall.
Follow the steps in RevenueCat’s Offering documentation to complete setup.
You should now have RevenueCat, Google Play Console, and App Store connect configured with subscriptions. Before continuing with the guide, navigate to the API keys section of RevenueCat dashboard and copy the Android and iOS SDK keys. We will need these in the next step to connect our app to RevenueCat.
Create a paywall
RevenueCat has paywall builder that allows you to remotely configure your entire paywall without any code changes or app updates. You can use paywalls to display different offerings and your app can multiple different types of paywalls, that you can further test with RevenueCat Experiments.
We are going to add a simple paywall to show our possible subscription options. You can find the Paywalls RevenueCat dashboard, in your project’s Monetization tools section. Pick from the pre-made paywall templates, or make a fully custom one, choose the offering we created in the previous sections, and once you are happy with the changes hit publish to make the paywall available for your app.
Step 4: Install and Configure RevenueCat
The prompt from before should have installed both react-native-purchases
and react-native-purchases-ui
in both already, to verify that check the package.json
file which you can find in the root of the project, it should have these:
1"dependencies": {
2 ....other dependencies...
3 "react-native-purchases": "^8.11.4",
4 "react-native-purchases-ui": "^8.11.4",
5 },
The versions should be 8.11.4 or higher. If either of these are missing or the version are lower than the mentioned, install the RevenueCat SDKs, by running:
1npx expo install react-native-purchases@8.11.4
2npx expo install react-native-purchases-ui@8.11.4
Initialize the RevenueCat SDK
You can now initialize RevenueCat in your app. This is typically done once when your app starts. Creating our app with Bolt should have created a commented section where to place the following code parts in our app. Most likely it is in a file called _layout.tsx
1export function initializePurchases() {
2 useEffect(() => {
3 const initRevenueCat = async () => {
4 try {
5 // Replace with your RevenueCat API key
6 const apiKey = Platform.select({
7 ios: 'your_ios_api_key_here',
8 android: 'your_android_api_key_here',
9 default: 'your_ios_api_key_here',
10 });
11
12 if (apiKey) {
13 await Purchases.configure({ apiKey });
14 console.log('RevenueCat initialized successfully');
15 }
16 } catch (error) {
17 console.error('Failed to initialize RevenueCat:', error);
18 }
19 };
20
21 initRevenueCat();
22 }, []);
23}
24
25
26// in _layout.tsx
27
28export default function RootLayout() {
29 useFrameworkReady();
30 initializePurchases();
31
32 return (
33 <>
34 <Stack screenOptions={{ headerShown: false }}>
35 <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
36 <Stack.Screen name="+not-found" />
37 </Stack>
38 <StatusBar style="auto" />
39 </>
40 );
41}
Paste the API keys you copied in the previous section of this guide into the apiKey part, to enable your app to interact with RevenueCat’s servers.
Step 5: Add purchase logic to the UI
To check if the user has an active subscription, query getCustomerInfo() and look for an active entitlement:
1
2export function useSubscriptionStatus() {
3 const [isSubscribed, setIsSubscribed] = useState(false);
4 const [isLoading, setIsLoading] = useState(true);
5
6 useEffect(() => {
7 const checkSubscriptionStatus = async () => {
8 try {
9 const customerInfo = await Purchases.getCustomerInfo();
10 const isActive = customerInfo.entitlements.active['premium'] !== undefined;
11 setIsSubscribed(isActive);
12 } catch (error) {
13 console.error('Failed to check subscription status:', error);
14 setIsSubscribed(false);
15 } finally {
16 setIsLoading(false);
17 }
18 };
19
20 checkSubscriptionStatus();
21
22 // Set up listener for purchase updates
23 Purchases.addCustomerInfoUpdateListener(checkSubscriptionStatus);
24
25 return () => {
26 Purchases.removeCustomerInfoUpdateListener(checkSubscriptionStatus);
27 };
28 }, []);
29
30 return { isSubscribed, isLoading };
31}
Use this in both the Feed and Profile tabs to reflect the user’s subscription status.
Implement the paywall logic
Now we can update the paywall screen to show available offerings and handle purchases:
1//... in index.tsx
2
3export default function Feed() {
4 const { isSubscribed, isLoading } = useSubscriptionStatus();
5
6 if (isLoading) {
7 return (
8 <SafeAreaView style={styles.container}>
9 <View style={styles.loadingContainer}>
10 <Text style={styles.loadingText}>Loading...</Text>
11 </View>
12 </SafeAreaView>
13 );
14 }
15
16 return (
17 <SafeAreaView style={styles.container}>
18 <View style={styles.header}>
19 <Text style={styles.headerTitle}>Cats Feed</Text>
20 {isSubscribed && (
21 <Text style={styles.subscriptionBadge}>Premium</Text>
22 )}
23 </View>
24
25 <ScrollView
26 style={styles.scrollView}
27 contentContainerStyle={styles.scrollContent}
28 showsVerticalScrollIndicator={false}
29 >
30 {catImages.map((imageUrl, index) => {
31 const isLocked = !isSubscribed && index >= 2;
32 return (
33 <OverlayedImage
34 key={index}
35 imageUrl={imageUrl}
36 isLocked={isLocked}
37 onPress={() => console.log(`Viewing cat image ${index + 1}`)}
38 />
39 );
40 })}
41 </ScrollView>
42 </SafeAreaView>
43 );
44}
This fetches the product offerings from RevenueCat and initiates a purchase flow. After a successful purchase, the entitlement is checked and the paywall is dismissed if the subscription is active.
Update the OverlayImage component
Finally, use the isLocked value in your OverlayImage component to lock or unlock content:
1export default function OverlayedImage({ imageUrl, isLocked, onPress }: OverlayedImageProps) {
2
3 const handlePress = async () => {
4 if (isLocked) {
5 await RevenueCatUI.presentPaywall();
6 } else if (onPress) {
7 onPress();
8 }
9 };
10
11 return (
12 <View style={styles.container}>
13 <TouchableOpacity onPress={handlePress} style={styles.imageContainer}>
14 <Image source={{ uri: imageUrl }} style={styles.image} />
15 {isLocked && (
16 <View style={styles.overlay}>
17 <Text style={styles.overlayText}>Subscribe to unlock</Text>
18 </View>
19 )}
20 </TouchableOpacity>
21 </View>
22 );
23}
This ensures that only subscribed users can access the full list of cat pictures.
Conclusion
With the RevenueCat and Google Play Console set up, the RevenueCat SDK integrated, and the necessary code changes made to our Bolt created app our app is now ready to accept and react to users subscribing. Create a new EAS build of your application with the same command we used before to create the first build of our app. Once the build finishes processing, test your app and the subscriptions. If you run into any problems, check that you have placed the code provided in the correct places. These guides can also possibly help you:
- Using Bolt with Expo to create mobile apps, covers everything you need to deploy new versions of your app
- RevenueCat’s updated guide to working with Expo, if you want to dive deeper into integrating your app with RevenueCat
- The ultimate guide to Android subscription testing, RevenueCat’s guide to testing Android subscriptions.
You might also like
- Blog post
A beginner’s guide to implementing ad-free subscriptions in your React Native app
A step-by-step tutorial to let users pay to remove ads—using Expo, AdMob, and RevenueCat
- Blog post
How to build a Blinkist-style paywall using RevenueCat webhooks and Zapier
Build a Blinkist-style paywall with RevenueCat and Zapier—no backend required.
- Blog post
Server-driven UI SDK on Android: how RevenueCat enables remote paywalls without app updates
Learning server-driven UI by exploring RevenueCat's Android SDK.