●RORKMAX — Rork Max generates pure Swift instead of React Native, enabling true native apps across iPhone, iPad, Watch, TV, Vision Pro, and iMessage●APPLE — Rork's 2026 direction has a clear theme of native empowerment across the Apple ecosystem●EXPO — Standard builds run on React Native and Expo, so you're left with a real project structure and code you can keep working on●FUNDING — Rork recently raised $15M and now sees over 743,000 monthly visits with 85% growth●PRICING — Rork is free to start, with paid plans from $25/month and Rork Max at $200/month●CROSS — Rork builds iOS, Android, and web from a single prompt, finished off with a bit of follow-up tweaking●RORKMAX — Rork Max generates pure Swift instead of React Native, enabling true native apps across iPhone, iPad, Watch, TV, Vision Pro, and iMessage●APPLE — Rork's 2026 direction has a clear theme of native empowerment across the Apple ecosystem●EXPO — Standard builds run on React Native and Expo, so you're left with a real project structure and code you can keep working on●FUNDING — Rork recently raised $15M and now sees over 743,000 monthly visits with 85% growth●PRICING — Rork is free to start, with paid plans from $25/month and Rork Max at $200/month●CROSS — Rork builds iOS, Android, and web from a single prompt, finished off with a bit of follow-up tweaking
Rork Max × RevenueCat Paywalls SDK: Remote Paywalls, A/B Testing, and Conversion Gains
A complete guide to integrating RevenueCat Paywalls SDK with Rork Max apps — enabling remote paywall updates and A/B testing without App Store reviews, with production-ready code examples and conversion optimization strategies.
One of the first walls you hit after shipping an app is this: "I just want to tweak the paywall copy, but now I have to go through App Store review again."
Change the button from "Start Free Trial" to "Try Free for 7 Days." Adjust how the price is displayed. Shift the emphasis on the annual plan discount. Every one of these small improvements means another 1–3 day wait for review — and a possible rejection. For indie developers, that friction is expensive in time and momentum.
RevenueCat Paywalls SDK solves this at the root. Paywall design, copy, pricing, and offers are all managed from a dashboard and pushed to users instantly — no app review needed. Built-in A/B testing lets you measure which paywall variant actually drives more subscriptions.
This guide walks through integrating this system into a Rork Max app with working code, real pitfalls, and the conversion principles that matter most.
Why RevenueCat Paywalls SDK Matters
In a traditional paywall setup, everything the user sees is baked into the app binary. Changing a single price label requires a code change → build → TestFlight → App Store review pipeline. The median subscription conversion rate across apps is roughly 2–3%. Moving that number to 5% roughly doubles revenue — but sustained improvement requires continuous iteration, and waiting for reviews after every change kills iteration speed.
RevenueCat Paywalls SDK takes a "remote-first" approach: the app fetches paywall configuration from the dashboard at runtime and renders accordingly. Design or copy changes propagate to users almost immediately, without touching the app binary.
Speaking from experience: once this is in place, "let's test three paywall variants this week" becomes a realistic plan rather than a month-long project.
What Paywalls SDK Can and Can't Do
What it handles: Visual paywall creation in a dashboard editor, remote updates without review, built-in A/B testing (Experiments), and rendering offering packages (plans) dynamically.
What it doesn't handle: Fully bespoke animations or deeply branded UI that goes beyond the SDK's template system. For those cases, you'll implement a custom paywall — but RevenueCat's Experiments feature still works with custom paywalls, so A/B testing remains available.
Paywalls SDK builds on top of RevenueCat's core subscription management SDK. If you haven't set up RevenueCat's basic IAP flow yet, do that first before continuing here.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Developers stuck waiting for App Store reviews to update their paywall can now make copy, design, and offer changes instantly from the RevenueCat dashboard — no review required
✦Learn the complete implementation from SDK setup to A/B test configuration and conversion analysis cycle, with production-ready code examples throughout
✦Apply data-backed paywall design principles that consistently improve subscription conversion rates to your own Rork Max app
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Grab both API keys from RevenueCat dashboard → API Keys. iOS and Android use separate keys.
SDK Initialization
Initialize in your app entry point (app/_layout.tsx or equivalent):
import Purchases, { LOG_LEVEL } from 'react-native-purchases';import { useEffect } from 'react';export default function RootLayout() { useEffect(() => { if (__DEV__) { Purchases.setLogLevel(LOG_LEVEL.DEBUG); } Purchases.configure({ apiKey: process.env.EXPO_PUBLIC_REVENUECAT_API_KEY ?? '', }); // Works for anonymous users too — no login required before initializing }, []); return <YourNavigationRoot />;}
Store your API key in an .env file as EXPO_PUBLIC_REVENUECAT_API_KEY and set the same variable in your EAS / Cloudflare environment config. Hardcoding it directly in the source means it gets committed to git — the kind of mistake that's easy to make and annoying to fix retroactively.
Building Your Paywall in the Dashboard
Creating Offerings
In RevenueCat dashboard → Monetization → Offerings, create the offering structures you want to test. An offering is the set of purchase packages presented to the user. Examples:
monthly_only: Just the monthly plan — clean and simple
annual_and_monthly: Annual + monthly — lets you emphasize the discount
trial_first: Free trial prominently featured up front
Creating multiple offerings is what enables A/B testing. "Discount-emphasis variant" vs "features-emphasis variant" can be defined as separate offerings and compared head to head.
Paywall Visual Editor
Add a Paywall to an offering to open the visual editor. You can customize:
Hero image and app icon
Headline and subtitle copy
Plan display format (cards, list, highlighted)
CTA button label and color
Feature list items ("✓ No ads", "✓ Up to 100 entries/month", etc.)
For localization, set language-specific copy under the Localization tab. Copy changes in any language also go live without review — useful for fine-tuning tone for different markets.
Displaying the Paywall in Rork Max
Basic Modal Presentation
The simplest integration uses the RevenueCatUI component:
import { useState } from 'react';import { View, Button } from 'react-native';import RevenueCatUI, { PAYWALL_RESULT } from 'react-native-purchases-ui';export function PremiumUpgradeScreen() { const [isLoading, setIsLoading] = useState(false); const handleShowPaywall = async () => { setIsLoading(true); try { const result = await RevenueCatUI.presentPaywall(); switch (result) { case PAYWALL_RESULT.PURCHASED: // Purchase complete — refresh entitlement and proceed console.log('Purchase complete'); break; case PAYWALL_RESULT.RESTORED: // Restore complete console.log('Restore complete'); break; case PAYWALL_RESULT.NOT_PRESENTED: // Offering not configured or paywall couldn't load console.warn('Paywall could not be displayed'); break; case PAYWALL_RESULT.CANCELLED: // User dismissed — no action needed break; case PAYWALL_RESULT.ERROR: console.error('Paywall error'); break; } } catch (error) { console.error('Unexpected paywall error:', error); } finally { setIsLoading(false); } }; return ( <View> <Button title="Upgrade to Premium" onPress={handleShowPaywall} disabled={isLoading} /> </View> );}
presentPaywall() automatically uses the Default Offering you've set in the dashboard. No offering identifier needed in most cases.
Targeting a Specific Offering
When running A/B tests, you can explicitly specify which offering to show:
import Purchases from 'react-native-purchases';import RevenueCatUI from 'react-native-purchases-ui';const showTargetedPaywall = async (offeringIdentifier: string) => { try { const offerings = await Purchases.getOfferings(); const targetOffering = offerings.all[offeringIdentifier]; if (!targetOffering) { // Graceful fallback to current offering if identifier not found await RevenueCatUI.presentPaywall(); return; } const result = await RevenueCatUI.presentPaywallIfNeeded({ offering: targetOffering, requiredEntitlementIdentifier: 'premium', }); return result; } catch (error) { // Silent failure — paywall simply doesn't appear, app continues normally console.error('Paywall fetch error:', error); }};
presentPaywallIfNeeded() is smart about users who already have the entitlement — it won't show the paywall to existing subscribers. Safe to call without first checking subscription status.
Reactive Entitlement Hook
A reusable hook for tracking subscription state across the app:
The listener fires the moment a purchase completes, so the UI updates without any manual refresh logic or polling.
Setting Up and Analyzing A/B Tests
Creating an Experiment
In RevenueCat dashboard → Experiments → Create new experiment:
Control (A): Current default offering
Treatment (B): The variant you want to test
Traffic split: 50/50 is standard and easiest to interpret statistically
Let tests run for at least two weeks, ideally a month. With low user volumes, short tests produce results you can't trust statistically — I've seen teams make the wrong call after just a few days and regret it.
Metrics That Matter
The Experiments analysis dashboard surfaces:
Conversion Rate: What percentage of users who saw the paywall purchased
MRR Impact: Actual revenue contribution from each variant
Initial Conversion: First-view purchase rate — the clearest signal of messaging quality
Retention: Are users sticking around after subscribing?
A common trap: conversion rate alone can be misleading. A variant that pushes a cheaper monthly plan might show higher conversion but lower MRR. Always evaluate both.
Patterns That Consistently Work
From looking at A/B test data across multiple apps:
Annual plan prominence: Placing the annual plan above the monthly with a "X% off" badge is often the highest-ROI first test to run. Users respond well to the framing of "this is the smart long-term choice."
Trial-first CTA: "Try free for 7 days" outperforms "Subscribe — $5.80/month" in nearly every test I've seen. The psychological shift from "spending money" to "trying for free" dramatically lowers barrier.
Feature list ordering: Move the feature users care most about to the top. "Unlimited storage" resonates more than "Advanced analytics" for most consumer apps. Test the order.
Common Implementation Mistakes and Fixes
Mistake 1: Empty Paywall Because Offering Isn't Configured
If presentPaywall() returns NOT_PRESENTED, the usual cause is a missing Default Offering or an offering with no packages. Debug quickly with:
Run this, then fix what's missing in the dashboard before touching the app code.
Mistake 2: Mixing Sandbox and Production Data
All simulator testing uses the Sandbox environment, but RevenueCat dashboard defaults to Production view. "I'm testing but the Experiments data isn't showing up" is almost always this: check the Sandbox/Production toggle at the top right of the dashboard.
Keep a habit of: sandbox view for development, production view for launch decisions.
Mistake 3: UI Not Updating After Purchase
Calling getCustomerInfo() right after a purchase requires a server round trip — there's a noticeable delay, during which the UI appears unchanged. The fix is addCustomerInfoUpdateListener (already in the hook above) combined with optimistic UI updates:
case PAYWALL_RESULT.PURCHASED: // Optimistic update — show the upgraded state immediately setIsPremiumOptimistic(true); // Background confirmation from RevenueCat servers refetchEntitlement(); break;
This makes the transition feel instantaneous even when the server round trip takes a second or two.
Mistake 4: Android Packages Missing from Offerings
iOS and Android require separate product IDs with different naming conventions. RevenueCat abstracts this through offerings, but you still need to attach both iOS (App Store) and Android (Google Play) packages to each offering in the dashboard.
When adding Android support to an iOS-first app, it's easy to forget to update existing offerings. The symptom: paywall shows on iOS, empty screen on Android.
Mistake 5: Inconsistent Trial Experience
Concern: users see Variant A, close the paywall, reopen the app, and see Variant B. RevenueCat Experiments handles this automatically — once a user is assigned to a variant, they stay in that variant until the experiment ends.
The edge case: uninstall → reinstall can result in a new anonymous user ID and potentially a different variant assignment. For strict statistical validity with high-value apps, run experiments only against authenticated users.
Paywall Design Principles That Drive Conversions
Frame Around Gains, Not Losses
"Sign up or ads will appear" is loss framing. "Sign up and enjoy an ad-free experience" is gain framing. For most audiences, gain framing converts better and feels less pressuring.
Practically:
❌ "Advanced features unavailable" → ✅ "Unlock all features"
❌ "Limited to 5 exports/month" → ✅ "Unlimited exports with Premium"
Specificity Over Vagueness
"Advanced analytics" means nothing. "Track up to 500 data points per month" is concrete. Dollar amounts also benefit from anchoring: "$4.99/month — less than a coffee" gives users a reference point.
Free Trial Position
If you offer a trial, lead with it. "7 days free, cancel anytime" in large text near the top of the paywall consistently outperforms burying it in the fine print. Users who haven't experienced your app's value need that "no risk" assurance most.
Always pair this with the disclosure of what happens after the trial ends — e.g., "After 7 days, $5.80/month (auto-renewing)" — both because App Store guidelines require it and because transparent pricing builds trust.
First-Screen Real Estate
Paywalls are viewed in vertical scroll. Whatever falls below the fold on a standard device gets seen by a fraction of users. Pack the value proposition — price, key benefit, trial period, CTA — into the first visible screen. RevenueCat's visual editor makes reordering elements straightforward, so test different first-screen arrangements as part of your experiments.
Production Readiness Checklist
Before shipping:
Dashboard side
Default Offering is set and includes packages for both iOS and Android
Paywall copy is finalized in all supported languages
Any active Experiment has correct traffic split and start conditions confirmed via preview
Error handling in place — paywall failure doesn't crash the app
Promotional offers (if applicable) are properly passed to presentPaywall(offering:)
App Store compliance
"Subscription auto-renews" disclosure visible on the paywall
Privacy Policy and Terms of Service links accessible from the paywall
Price and renewal period clearly stated (required to avoid review rejection)
Building a Custom Paywall That Still Works with Experiments
The template-based editor covers most use cases, but some Rork Max apps need a fully custom paywall — a unique visual style, micro-animations, or an onboarding flow that leads directly into the purchase decision. RevenueCat supports this through the PaywallView component and the presentPaywallIfNeeded API, both of which work with Experiments even when you're rendering the UI entirely yourself.
When a user is enrolled in an Experiment, offerings.current automatically returns the variant they've been assigned to. You don't need to know which experiment is running or which variant to show — RevenueCat handles the assignment and returns the right offering transparently.
Custom Paywall Component Structure
Here's a pattern for building a fully custom paywall that stays compatible with RevenueCat's Experiments:
The key insight here is that offering.availablePackages is determined by what you set in the RevenueCat dashboard — so swapping in a different set of packages (say, removing the monthly option) is a dashboard change, not a code change. Your custom UI adapts automatically.
Integrating Paywalls into Your App's User Flow
Where you surface the paywall matters as much as what it says. Three effective patterns:
Pattern 1: Feature Gate with Contextual Prompt
Show the paywall when a user tries to access a premium feature, with messaging that's specific to that feature:
const handlePremiumFeaturePress = async () => { const { status } = usePremiumStatus(); if (status === 'active') { // Navigate to the feature directly router.push('/premium-feature'); return; } // User isn't subscribed — show paywall with context const result = await RevenueCatUI.presentPaywall({ // The 'offering' parameter lets you show a specific offering // configured for this feature's value proposition }); if (result === PAYWALL_RESULT.PURCHASED) { router.push('/premium-feature'); }};
Contextual paywalls — where the user already wants the feature — consistently outperform paywalls shown at arbitrary moments like app launch.
Pattern 2: Onboarding Paywall
Show the paywall at the end of an onboarding sequence, after the user has seen the app's core value:
// In your onboarding completion screenconst handleOnboardingComplete = async () => { // Check if user is already subscribed (came from a paid campaign, etc.) const result = await RevenueCatUI.presentPaywallIfNeeded({ requiredEntitlementIdentifier: 'premium', }); // Whether they subscribed or dismissed, proceed to main app router.replace('/(tabs)/home');};
presentPaywallIfNeeded skips the paywall entirely if the user already has the entitlement — making it safe to call at onboarding completion without needing to check subscription status first.
Pattern 3: Soft Wall with Preview
Let users access a limited version of a premium feature, then prompt after they've experienced value:
// Show feature but with a usage limit, then promptconst [usageCount, setUsageCount] = useState(0);const FREE_USAGE_LIMIT = 3;const handleFeatureUse = async () => { const newCount = usageCount + 1; setUsageCount(newCount); if (newCount >= FREE_USAGE_LIMIT) { // User has seen enough value — show paywall await RevenueCatUI.presentPaywall(); return; } // Proceed with feature executeFeature();};
This pattern works particularly well for AI features where the first few uses demonstrate clear value. Users who've already seen results convert at much higher rates than users shown a paywall on first encounter.
Handling Edge Cases in Production
Subscription Status on App Resume
When the app returns from background, subscription status can be stale. Users who cancel or whose billing fails may still appear as active subscribers in a cached CustomerInfo object. Add a refresh on app foregrounding:
import { AppState, AppStateStatus } from 'react-native';import { useEffect, useRef } from 'react';import Purchases from 'react-native-purchases';export function useEntitlementRefreshOnForeground() { const appStateRef = useRef<AppStateStatus>(AppState.currentState); useEffect(() => { const subscription = AppState.addEventListener('change', async (nextState) => { if ( appStateRef.current.match(/inactive|background/) && nextState === 'active' ) { // Silently refresh — the CustomerInfoUpdateListener handles the UI update try { await Purchases.getCustomerInfo(); } catch { // Don't block the app if refresh fails } } appStateRef.current = nextState; }); return () => subscription.remove(); }, []);}
Add this hook to your root layout. It ensures that a user who cancelled their subscription in the App Store settings sees the correct (downgraded) state when they return to your app.
Promotional Offer Handling
RevenueCat supports promotional offers — discounted pricing for win-back campaigns targeting lapsed subscribers. Presenting these correctly requires passing the offer to the purchase call:
Promotional offers are configured in App Store Connect (for iOS) and require Apple's promotional offer signing in RevenueCat. Don't attempt to fetch and apply promo offers without first verifying eligibility — applying a promo to an ineligible user throws a StoreKit error.
Reading Experiment Data and Making Decisions
After your first A/B test has run for two weeks, you'll be looking at the Experiments dashboard trying to decide which variant won. A few principles that help avoid common interpretation mistakes:
Statistical significance isn't everything. RevenueCat Experiments shows confidence intervals, but with the user volumes typical of indie apps (hundreds rather than tens of thousands of monthly actives), you'll rarely hit 95% confidence within a reasonable test window. A pragmatic approach: if after three weeks one variant shows meaningfully higher conversion (say, 3.2% vs 2.1%) with overlapping but distinct confidence intervals, it's often reasonable to promote the better-performing variant and run the next test. Perfect certainty isn't required to make progress.
Watch for interaction effects. Running two Experiments simultaneously can produce confounded results — a user enrolled in both A/B tests is exposed to combinations you didn't plan for. RevenueCat doesn't prevent this automatically. Keep one Experiment active at a time, especially early on.
Seasonal variation matters. An Experiment running over a holiday period will produce different conversion rates than one running in January. If your results look anomalous, check whether the test period overlapped with any unusual traffic spikes (a Product Hunt launch, a press mention) that could skew the data.
Track revenue, not just conversions. A variant with 15% higher conversion but 40% lower average revenue per user (because it pushes a cheaper plan too hard) is a net loss. Always look at MRR impact alongside conversion rate in the Experiments dashboard before declaring a winner.
Once you've identified a winning variant, promote it to Default Offering in the dashboard and immediately set up the next test. A cadence of one completed experiment per month means 12 data-driven improvements per year — compounding into meaningful revenue gains over time.
RevenueCat Paywalls SDK vs. Building from Scratch
A question that comes up often: why not just build the paywall entirely in Rork Max, update it through OTA (Over-The-Air) updates via EAS Update, and skip the RevenueCat Paywalls SDK entirely?
OTA updates can push JS bundle changes without going through App Store review, which seems to solve the same problem. The meaningful differences:
A/B testing infrastructure. Building an A/B testing system from scratch — random user assignment, consistent assignment across sessions, statistical analysis — is a significant engineering investment. RevenueCat Experiments provides this out of the box.
Offering-paywall linkage. The SDK automatically resolves which offering (and therefore which paywall) to show based on Experiment enrollment, user eligibility, and the Default Offering fallback. Replicating this logic manually means maintaining it yourself when RevenueCat adds new features.
App Store compliance. RevenueCat's SDK handles StoreKit and Google Play Billing compliance, including promotion offer eligibility verification and the technical requirements around subscription disclosure. Building this correctly from scratch has a non-trivial surface area for errors.
The counter-argument for custom builds: full control over animation, layout, and design polish that goes beyond templates. For apps where brand consistency is a core part of the user experience, the custom paywall approach (still using RevenueCat for purchase logic and Experiments) is a reasonable choice.
Closing Thoughts
The biggest shift RevenueCat Paywalls SDK brings isn't technical — it's psychological. Once paywall iteration is decoupled from app review cycles, the "I should test that someday" improvements actually get tested.
The practical recommendation: start with one A/B test comparing your current paywall against a variant where the annual plan is more visually prominent. Takes about 30 minutes to set up. Run it for two weeks. The data will tell you more than months of guessing.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.