AI paywall screen

ProReact Native · Expo

A 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.

View as Markdown

Installation

$ npx shadcn@latest add @goodui-native-pro/ai-paywall-screen

Then 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

Use it for
  • 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.
Reach for something else
  • Monthly and yearly plans without credits: use paywall-screen.
  • One-off credit packs: this screen sells subscriptions only.

API reference

PropTypeDefault
title*string—
subtitlestring—
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.
number1.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—
labelsPartial<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.