RORK LABJP
TEST — The Rork Companion app lets you test on a real iPhone without a paid Apple Developer accountCLOUD — Code compiles on a cloud Mac, streaming a 60fps live simulator with real touch inputBROWSER — Design, code, and test entirely in Chrome or Safari — no Xcode requiredPUBLISH — Two-click App Store publishing keeps the submission process simpleMAX — Rork Max builds native Swift apps for iPhone, iPad, Apple Watch, and Vision ProRN — Standard Rork generates iOS and Android apps together with React Native (Expo)TEST — The Rork Companion app lets you test on a real iPhone without a paid Apple Developer accountCLOUD — Code compiles on a cloud Mac, streaming a 60fps live simulator with real touch inputBROWSER — Design, code, and test entirely in Chrome or Safari — no Xcode requiredPUBLISH — Two-click App Store publishing keeps the submission process simpleMAX — Rork Max builds native Swift apps for iPhone, iPad, Apple Watch, and Vision ProRN — Standard Rork generates iOS and Android apps together with React Native (Expo)
Articles/Dev Tools
Dev Tools/2026-04-15Intermediate

Rork + Superwall: A/B Test Your Paywall to Boost Subscription Conversion

Learn how to integrate Superwall into your Rork app to A/B test paywall designs and increase subscription conversion rates. Covers SDK setup, campaign configuration, RevenueCat integration, and analytics — with working code examples.

Rork470Superwall2Paywall3A/B Testing7Subscription23Monetization32React Native189

After launching a subscription app, one of the first walls developers hit is paywall conversion. The pricing seems right, users are engaged — but they're not tapping "Subscribe." Is it the design? The copy? The price anchoring? Without data, you're left guessing.

Superwall is an SDK built specifically for paywall A/B testing. It lets you swap paywall designs and copy from a dashboard without redeploying your app, then tells you exactly which variant converts better. This guide covers integrating Superwall into a Rork-generated React Native app, running your first A/B test, and avoiding the implementation pitfalls that trip up most developers.

What Superwall Does (and Why It Matters for React Native Apps)

Superwall decouples the paywall screen from your app's code. Instead of hardcoding your paywall UI in a component, you register "placements" — trigger points in your app where a paywall might appear — and the dashboard controls what gets shown to whom.

This solves three real problems:

  • Changing paywall copy no longer requires an App Store submission
  • You can run multiple variants simultaneously with automatic traffic splitting
  • Every user interaction with the paywall is tracked: views, dismissals, conversions, and which placement triggered the paywall

The React Native SDK works well with Expo, though you'll need an Expo Development Build (not Expo Go) since Superwall uses native modules.

Setup: Superwall Account and App Registration

Create an account at superwall.com and register your app. You'll receive separate API keys for iOS and Android — keep both handy.

If you're using RevenueCat for subscription management (the recommended approach), connect it in the Superwall dashboard under Integrations before writing any code. Superwall will pull product and subscription status data from RevenueCat automatically.

Installing the SDK

npm install @superwall/superwall-reactnative
# or
yarn add @superwall/superwall-reactnative

Since Superwall uses native modules, you'll need a development build:

eas build --profile development --platform ios

Initializing Superwall

In your app entry point (App.tsx or app/_layout.tsx):

import Superwall from '@superwall/superwall-reactnative';
import { Platform } from 'react-native';
 
const SUPERWALL_API_KEY = Platform.OS === 'ios'
  ? 'pk_ios_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
  : 'pk_android_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
 
// Store these in environment variables in production:
// process.env.EXPO_PUBLIC_SUPERWALL_API_KEY_IOS
 
Superwall.configure(SUPERWALL_API_KEY);

configure queues internally, so you don't need to await it before registering placements elsewhere in your app.

Triggering a Paywall with Placements

The core concept is the placement — a named trigger point you call when a user hits a premium feature gate.

import Superwall from '@superwall/superwall-reactnative';
 
const handlePremiumFeature = async () => {
  await Superwall.shared.register('premium_feature_tapped');
  // Code after register() only runs if the user is subscribed
  // Superwall checks subscription status automatically
  navigateToPremiumFeature();
};

The string 'premium_feature_tapped' maps to a campaign in the Superwall dashboard. When a user hits this code path, Superwall checks: Is there an active campaign for this placement? Is this user in the test audience? Which variant should they see?

Use Different Placements for Different Contexts

This is where most developers leave conversion gains on the table. The placement that works best for someone trying to use the AI chat feature is different from the one that works for someone exploring the settings screen.

// Define placements that match where the user is in their journey
const placements = {
  aiChatGate: 'ai_chat_gate',           // Trying to use a gated feature
  exportGate: 'export_feature_gate',     // Trying to export data
  settingsUpgrade: 'settings_upgrade',   // Explicitly looking at upgrade options
  onboardingEnd: 'onboarding_paywall',   // Right after completing onboarding
};
 
// Usage: match the placement to the context
await Superwall.shared.register(placements.aiChatGate);

Why this matters: a user who just tried to use AI and was blocked is primed for a feature-focused pitch ("Unlock AI for unlimited messages"). A user sitting in Settings is more likely to respond to a value-summary pitch ("Everything in one plan").

RevenueCat Integration: The PurchaseController

If you're using RevenueCat, implement a PurchaseController to delegate purchase handling to it. Superwall handles the display; RevenueCat handles the transaction.

import Superwall, {
  PurchaseController,
  PurchaseResult,
  RestorationResult,
} from '@superwall/superwall-reactnative';
import Purchases from 'react-native-purchases';
 
class RCPurchaseController extends PurchaseController {
  async purchaseFromAppStore(productId: string): Promise<PurchaseResult> {
    try {
      const { customerInfo } = await Purchases.purchaseStoreProduct(
        { productIdentifier: productId } as any
      );
 
      // Check if the expected entitlement is now active
      if (customerInfo.entitlements.active['pro'] \!== undefined) {
        return PurchaseResult.Purchased;
      }
      return PurchaseResult.Cancelled;
    } catch (error: any) {
      if (error.userCancelled) {
        return PurchaseResult.Cancelled;
      }
      return PurchaseResult.Failed;
    }
  }
 
  async restorePurchases(): Promise<RestorationResult> {
    try {
      const customerInfo = await Purchases.restorePurchases();
      const hasActiveEntitlements =
        Object.keys(customerInfo.entitlements.active).length > 0;
      return hasActiveEntitlements
        ? RestorationResult.Restored
        : RestorationResult.NothingToRestore;
    } catch {
      return RestorationResult.Failed;
    }
  }
}
 
// Pass the controller during configuration
const controller = new RCPurchaseController();
Superwall.configure(SUPERWALL_API_KEY, { purchaseController: controller });

Replace 'pro' with your actual RevenueCat entitlement identifier. Getting PurchaseResult wrong is the most common integration bug — if you return Purchased when the entitlement isn't actually active, Superwall unlocks the content incorrectly. If you return Failed when the user actually cancelled, the UX breaks.

Setting Up an A/B Test in the Dashboard

Once your code is deployed, the actual test configuration happens in the dashboard.

Creating Paywall Variants

Under "Paywalls," create two versions. Superwall provides HTML/CSS templates you can customize directly. For your first test, change only one element — the headline. Two variables in one test means you can't attribute the result to either change confidently.

A simple first test:

  • Variant A (control): "Upgrade to Premium"
  • Variant B (test): "Remove the limits — go unlimited"

Campaign Configuration

Create a Campaign, attach it to your placement, and set the traffic split. 50/50 is standard for a first test. Under "Audience," you can target by:

  • Session count (e.g., only users on their 3rd session or later)
  • User attributes you set via Superwall.shared.setUserAttributes()
  • Device and locale

Reading Results

Don't make decisions too early. Superwall shows confidence intervals alongside conversion rates. Practical guidelines:

  • Wait until each variant has at least 100–200 paywall views
  • Look for p < 0.05 before drawing conclusions
  • Run tests for at least 7 days to capture weekly behavioral patterns

Forwarding Superwall Events to Your Analytics Stack

Superwall's own analytics are solid, but connecting them to your existing analytics tool lets you correlate paywall behavior with upstream events (like which onboarding step the user completed).

import Superwall, {
  SuperwallEventInfo,
  SuperwallDelegate,
} from '@superwall/superwall-reactnative';
 
class AnalyticsDelegate extends SuperwallDelegate {
  async handleSuperwallEvent(eventInfo: SuperwallEventInfo): Promise<void> {
    const { event } = eventInfo;
 
    // Forward key events to your analytics platform
    switch (event.type) {
      case 'paywallOpen':
        analytics.track('Paywall Viewed', {
          placement: event.paywallInfo.identifier,
          experimentId: event.paywallInfo.experiment?.id,
          variantId: event.paywallInfo.experiment?.variant.id,
        });
        break;
 
      case 'transactionComplete':
        analytics.track('Subscription Started', {
          placement: event.paywallInfo.identifier,
          productId: event.transaction?.productIdentifier,
        });
        break;
 
      case 'paywallClose':
        analytics.track('Paywall Dismissed', {
          placement: event.paywallInfo.identifier,
        });
        break;
    }
  }
}
 
Superwall.shared.delegate = new AnalyticsDelegate();

Common Mistakes to Avoid

Paywall not showing in development

Superwall won't display anything if no active campaign exists for the placement you're calling. Confirm in the dashboard that your paywall is in Active state and that it's attached to a campaign targeting the correct placement.

Always seeing the same variant during testing

Superwall assigns variants based on user identity. To see a different variant on the same device, call Superwall.shared.reset() or use Preview Mode in the dashboard to force a specific variant.

Worrying about double charges with RevenueCat

With a proper PurchaseController, RevenueCat processes the transaction exactly once. Superwall tracks which paywall triggered the purchase but has no involvement in the actual charge. The risk of double-charging is in returning the wrong PurchaseResult — specifically, accidentally returning Purchased before the RevenueCat entitlement is confirmed active.

The Mindset Shift: Paywalls as a Product, Not a Checkbox

Most developers treat the paywall as something to build once and ship. Superwall makes it practical to treat it as a product — something to iterate on with real data the same way you iterate on any other screen.

A single headline change improving conversion by 20–30% is not unusual. Multiply that by the lifetime value of a subscriber, and a few hours of A/B testing setup pays for itself many times over.

Start small: integrate the SDK, add one placement around your most valuable gated feature, and run a single headline test. Once you see your first statistically significant result, you'll have a clear sense of how much headroom remains.

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.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-04-21
Building a Settings Screen That Actually Works in Your Rork App — Notifications, Subscription Management, and Legal Requirements
A practical guide to building production-grade settings screens for your Rork app, covering notifications, subscription management, legal disclosures, and support flows — with real code examples.
Business2026-04-09
Complete Guide to Paywall Optimization & Onboarding Design for Rork Apps — Free Trial Strategy, Conversion Psychology, and A/B Testing to Triple Your Revenue
A complete framework for maximizing Rork app subscription revenue. Covers free trial strategy, conversion psychology, paywall placement, and RevenueCat A/B testing to scientifically improve your paid conversion rate.
Dev Tools2026-06-27
Before a Free Preview Walks Out via Screenshot: Detecting Screenshots and Screen Recording in Rork/Expo
How to protect paid preview images from screenshots and screen recording in a Rork/Expo app: the limits of expo-screen-capture, native isCaptured monitoring, and an iOS/Android-aware blur design.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →