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: the Goodspeed generation pipeline · Subscriptions and In-App Purchases
REAL GENERATED CODE
A snippet from a RevenueCat app the studio shipped
This pattern comes straight out of the production groundwork every Goodspeed app is generated on. The studio generates RevenueCat code like this for every app in the pipeline, not a hello-world sample you have to grow into a real app.
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