AI paywall screen
ProReact Native · ExpoA paywall for AI apps with weekly and yearly plans that each carry a credit allowance. The billed amount is always the largest price, weekly math stays in small text, the auto-renew line sits next to the buy button, and Terms, Privacy and Restore are always shown. check-kit fails if a price variant breaks this.
Installation
$ npx shadcn@latest add @goodui-native-pro/ai-paywall-screenThen run npx expo install --fix so react-native-reanimated match your Expo SDK. Files land in components/goodui-native/ai-paywall-screen.
Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.
Usage
// app/paywall.tsx (presented as a modal route)
import { Image, Linking } from "react-native";
import { router } from "expo-router";
import { AiPaywallScreen } from "@/components/goodui-native/ai-paywall-screen/ai-paywall-screen";
export default function Paywall() {
return (
<AiPaywallScreen
title="Keep creating"
subtitle="Your first portrait is ready. Plans include credits for every render."
benefits={["HD exports", "No watermark", "Every style"]}
defaultPlanId="year"
hero={<Image source={result} style={{ width: "100%", height: "100%" }} accessibilityIgnoresInvertColors />}
plans={[
{ id: "year", period: "year", name: "Yearly", price: 59.99, credits: 2400, savePercent: 62 },
{ id: "week", period: "week", name: "Weekly", price: 5.99, credits: 40, trialDays: 3 },
]}
onClose={() => router.back()}
onRestore={() => restorePurchases()}
onTerms={() => Linking.openURL(TERMS_URL)}
onPrivacy={() => Linking.openURL(PRIVACY_URL)}
onSubscribe={(planId) => purchase(planId)}
/>
);
}When to use
- Selling a subscription where each generation has a real cost, so a plan grants credits.
- Right after a first result, with that result in the hero slot.
- Monthly and yearly plans without credits: use paywall-screen.
- One-off credit packs: this screen sells subscriptions only.
API reference
| Prop | Type | Default |
|---|---|---|
| title* | string | — |
| subtitle | string | — |
| benefits* What the person gets, in order of pull. Keep to three or four lines. | string[] | — |
| plans* | AiPlan[] | — |
| defaultPlanId Plan selected at open. | string | — |
| currency ISO currency code. | string | "USD" |
| hero Art behind the title: a result the person just made is the strongest choice. | ReactNode | — |
| closeDelay Seconds before the close button appears. 0 shows it at once. Stores require a way out either way. | number | 1.5 |
| onClose* | () => void | — |
| onSubscribe* Return a promise: the buy button shows loading until it settles, and an error state if it rejects. | (planId: string) => unknown | — |
| onRestore* Required by the stores, so always shown. | () => unknown | — |
| onTerms* Opens your terms of use. Always shown. | () => void | — |
| onPrivacy* Opens your privacy policy. Always shown. | () => void | — |
| onNotNow Shows a quiet Not now link. Leave the paywall from here; do not open a second offer. | () => void | — |
| labels | Partial<AiPaywallLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
Motion and haptics
- Text and benefits rise in with a short stagger, the selected plan's dot springs in, and the buy button breathes slowly.
- The close button fades in after closeDelay seconds.
- Reduced motion: content fades in and the buy button holds still.
- Haptic: Choosing a plan uses the select cue; the buy button uses confirm.
- The breathing buy button is one transform on the UI thread.
Accessibility
- The title is a header and the plans are a radio group; each radio reads its name, price and credits.
- Close and Restore are labeled buttons; Terms and Privacy are links. All have touch targets larger than their text.
Notes for AI
- Pass the store's localized string in `priceLabel`. `price` stays a number for the weekly math.
- Do not add prices outside planPricing or restyle them: Apple requires the billed amount to be the most conspicuous pricing element, and scripts/check-paywall.ts enforces it.
- Return a promise from onSubscribe so the button shows loading and errors.
- savePercent is a number you state: compute it from your real prices.
- onRestore, onTerms and onPrivacy are required: point them at real pages before you ship.
- Keep closeDelay short. Stores require a way out.
- The screen does not grant credits: call credits.renew in onSubscribe after the purchase succeeds.
- onNotNow shows a quiet Not now link. Use it to leave the paywall, never to open a second offer.
Related
A complete subscription paywall: a monthly and yearly toggle with a savings badge, plan cards whose prices roll digit by digit when the period changes, a feature list, a trial note that always states the price, restore purchases and an async subscribe button.
A balance pill for an app bar whose digits roll when the balance changes and which turns warning colored when low, plus CreditsCost, a live price line that says what a job costs and what is left after it.
A consent screen shown before personal data reaches a third-party AI service: it names the provider, lists what is sent and what is not, has an optional age tick, and offers a visible way to decline.
A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.