@neko-os/rc-subscription
v0.3.0
Published
Drop-in RevenueCat subscription module for React Native (Expo) apps built with `@neko-os/ui`. Provides a full paywall, subscription gating, and subscription management — with i18n support for 26 languages.
Downloads
342
Readme
neko-rc-subscription
Drop-in RevenueCat subscription module for React Native (Expo) apps built with @neko-os/ui. Provides a full paywall, subscription gating, and subscription management — with i18n support for 26 languages.
Peer Dependencies
react-native-purchases(RevenueCat SDK)@neko-os/ui@react-navigation/nativereact-native-reanimatedreact-native-safe-area-contexti18nextdayjs
Configuration
Environment variables (or defaults):
| Variable | Default | Description |
|----------|---------|-------------|
| EXPO_PUBLIC_RC_API_KEY_IOS | — | RevenueCat API key for iOS |
| EXPO_PUBLIC_RC_API_KEY_ANDROID | — | RevenueCat API key for Android |
| EXPO_PUBLIC_RC_ENTITLEMENT_ID | premium | RevenueCat entitlement identifier |
Platform-specific API key selected automatically via Platform.OS.
Setup
1. Register locales
Call once at app boot, after i18n is initialized:
import { registerSubscriptionLocales } from 'neko-rc-subscription'
registerSubscriptionLocales(i18n)2. Wrap your app
import { SubscriptionHandler } from 'neko-rc-subscription'
<SubscriptionHandler
paywallConfig={{
title: 'Unlock Full Access',
subtitle: 'Track your habits without limits',
image: require('./assets/paywall-hero.png'),
features: [
{ label: 'paywall.unlimitedGoals', icon: 'flag-fill', free: false },
{ label: 'paywall.analytics', icon: 'bar-chart-fill', free: false },
{ label: 'paywall.basicTracking', icon: 'check-line', free: true },
],
}}
>
<App />
</SubscriptionHandler>3. Add routes
import { ActiveSubscriptionView } from 'neko-rc-subscription'
// Stack navigator
<Stack.Screen name="subscription/active" component={ActiveSubscriptionView} />Exports
Components
| Export | Description |
|--------|-------------|
| SubscriptionHandler | Context provider. Configures RevenueCat, fetches offerings and customer info, provides subscription state to the tree. |
| SubscriptionRequired | Gate component. Shows children if subscribed, otherwise renders the Paywall. |
| SubscriptionRequiredCTA | Soft gate. Renders children with a blurred overlay and "Unlock" button when not subscribed. |
| Paywall | Full paywall screen with hero image, feature comparison, plan selection, purchase and restore. |
| ActiveSubscriptionView | Subscription management screen showing plan details and expiry. |
Hooks
| Export | Description |
|--------|-------------|
| useSubscription() | Full subscription context: { isSubscribed, isLoading, customerInfo, offerings, paywallConfig, refresh } |
| useIsSubscribed() | Shorthand boolean — true when active entitlement exists. |
| useSubscribedAction(fn?) | Returns an action wrapper that runs fn when subscribed, otherwise opens the paywall. See Gating an action. |
Functions
| Export | Description |
|--------|-------------|
| registerSubscriptionLocales(i18n) | Registers the subscription namespace into an existing i18next instance for all 26 supported languages. |
SubscriptionHandler Props
| Prop | Type | Description |
|------|------|-------------|
| paywallConfig | object | Optional paywall customization (see below). |
paywallConfig
| Key | Type | Description |
|-----|------|-------------|
| title | string | Hero title. Falls back to t('paywall.title'). |
| subtitle | string | Hero subtitle. Falls back to t('paywall.subtitle'). |
| image | ImageSource | Hero image (parallax). Omit for text-only hero. |
| features | Feature[] | Feature comparison rows. Each: { label, icon?, free? }. label is a i18n key. free marks whether available on the free plan. |
SubscriptionRequired Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| disabled | bool | false | Bypass the gate (always show children). |
| modal | bool | — | Paywall rendered as modal (close icon instead of back arrow). |
| showReturn | bool | — | Show return icon on the paywall. |
| footerPaddingB | number | — | Override footer bottom padding. |
SubscriptionRequiredCTA Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| disabled | bool | false | Bypass the gate. |
| size | string | 'xs' | Button size. |
| buttonProps | object | — | Extra props forwarded to the unlock Button. |
Gating an action
useSubscribedAction wraps any callback so it only fires for subscribed users — otherwise the paywall opens (navigates to subscription/active). Use it for one-off actions that don't warrant a full gate component (SubscriptionRequired / SubscriptionRequiredCTA).
Two equivalent call styles:
import { useSubscribedAction } from 'neko-rc-subscription'
// Bind the fn up front
function ExportButton() {
const exportData = useSubscribedAction(() => doExport())
return <Button label="Export" onPress={exportData} />
}
// Bind nothing — pass the fn at call time (handy when the callback varies)
function Row({ item }) {
const run = useSubscribedAction()
return <Button label="Pin" onPress={() => run(() => pin(item))} />
}While subscription state is still loading (isLoading), the action is a no-op — it neither runs the fn nor opens the paywall, so an already-subscribed user is never bounced to the paywall during init.
Arguments are forwarded to the wrapped fn, and its return value is passed through (or undefined when the paywall is shown instead):
const save = useSubscribedAction(saveReport)
save(reportId) // -> saveReport(reportId) when subscribedPackage Support
The paywall auto-renders available packages from the current RevenueCat offering. Supported package types:
- Annual — shows per-month price and save percentage vs monthly
- Monthly — base reference price
- Lifetime — one-time purchase
Free trial detection is automatic from introPrice metadata.
i18n
Namespace: subscription
26 languages: cs, da, de, el, en, es, fi, fr, hi, hu, id, it, ja, ko, nl, no, pl, pt, ro, ru, sv, th, tr, uk, vi, zh.
Translation keys are organized under settings, paywall, cta, and active groups. See locales/en.js for the full key reference.
