Build apps with RevenueCat
RevenueCat is wired into every Goodspeed app that has in-app purchases: native SDK, paywall screen, subscription hooks, and PostHog analytics. Goodspeed generates RevenueCat as a standard part of every app, so the output is a working codebase from day one, not a scaffold you have to finish yourself.
WHAT GETS GENERATED
Built into every RevenueCat build
Every app Goodspeed generates with RevenueCat includes these production-ready patterns, wired together from the first build.
| Item | Description | Strength |
|---|---|---|
| lib/revenuecat.ts conditional native import | react-native-purchases is imported via require() at runtime so the bundle does not crash on web (where IAP is unavailable), and the module is a no-op instead. | Architecture |
| hooks/useSubscription.ts entitlement hook | A React hook that reads the user's active entitlements from RevenueCat and returns isPremium, tier, and expiresAt for any component that needs paywall checks. | Hook |
| hooks/usePaywall.ts purchase flow hook | Wraps the RevenueCat offerings fetch and Purchases.purchasePackage call. Handles loading state, error handling, and PostHog event tracking in one place. | Hook |
| app/(modal)/paywall.tsx paywall screen | A full-screen modal paywall with offerings fetched from RevenueCat, feature list, pricing tiers, and a restore purchases button. | Screen |
| gasConfig.backend.revenuecat key config | iOS and Android RevenueCat API keys live in gas.config.ts under backend.revenuecat and are read by lib/revenuecat.ts on initialization. | Configuration |
Source: gas-template repository · Subscriptions and In-App Purchases
REAL GENERATED CODE
A snippet from a RevenueCat app the studio shipped
This pattern comes directly from the gas-template codebase, the foundation every Goodspeed app is generated on. The studio generates RevenueCat code like this for every app in the pipeline, not just a hello-world scaffold.
Conditional IAP init
// lib/revenuecat.ts: conditional native import + init let Purchases: typeof import('react-native-purchases').default | null = null; if (!isWeb) { try { Purchases = require('react-native-purchases').default; } catch { /* module not available */ } } export async function initRevenueCat(): Promise<void> { if (isWeb || !Purchases) return; if (!gasConfig.features.inAppPurchases.enabled) return; const apiKey = Platform.OS === 'ios' ? gasConfig.backend.revenuecat.iosKey : gasConfig.backend.revenuecat.androidKey; if (!apiKey) return; await Purchases.configure({ apiKey }); }
Today's log