# Paywall Sheet

Subscription paywall sheet that can grow out of the button that opened it, with plan cards and a monthly price that rolls as you switch. A clear trial timeline shows when you’ll be reminded and charged.

> Composed · Pro · Mobile-Native — React Native, Expo Go incompatible (dev build required).

## Install

```bash
npx @lumioui/cli add pro/paywall-sheet
```

## Import

```tsx
import { defaultPaywallTimeline, PaywallSheet } from "@/components/ui/paywall-sheet/paywall-sheet";
```

## Props

| Prop | Type | Notes |
| --- | --- | --- |
| `visible` | `boolean` | — |
| `onClose` | `() => void` | — |
| `onSubscribe` | `(plan: PaywallPlan) => void | Promise<void>` | Start the purchase. Resolve on success; reject (e.g. offline, store  refused) and the CTA flickers once with the error message inline. |
| `plans?` | `PaywallPlanOption[]` | Plans, shown left to right. Default yearly (7-day trial) / monthly / lifetime. |
| `selectedPlan?` | `PaywallPlan` | Controlled selected plan. |
| `defaultSelectedPlan?` | `PaywallPlan` | Uncontrolled initial plan. Default: the first plan. |
| `onPlanChange?` | `(plan: PaywallPlan) => void` | — |
| `timeline?` | `(plan: PaywallPlanOption, start: Date) => PaywallTimelineStep[]` | Overrides the default trial / renewal timeline for a plan. |
| `startDate?` | `Date` | Day the timeline counts from. Default today. |
| `origin?` | `PaywallOrigin | null` | Grow out of this rect instead of rising from the bottom edge. |
| `onDismissed?` | `() => void` | Fires once the exit has finished and the sheet has unmounted. |
| `successLabel?` | `string` | CTA result label after a purchase. Default "Trial started" / "You're subscribed" / "It's yours". |
| `onSubscribed?` | `(plan: PaywallPlan) => void` | Called after the result has held (~700ms). Close the sheet and unlock  here. Without it, the sheet closes itself via `onClose`. |
| `refusedMessage?` | `string` | Message when `onSubscribe` rejects without an Error message. |
| `legal?` | `string` | Small print under the CTA. Default from the selected plan. |
| `purchasing?` | `PaywallPlan | null` | Externally driven purchase in flight (the CTA shows busy dots). |
| `onRestore?` | `() => void | Promise<void>` | Restore-purchases handler — renders "Restore" in the footer row. |
| `restoring?` | `boolean` | Restore in flight. |
| `onTermsPress?` | `() => void` | — |
| `onPrivacyPress?` | `() => void` | — |
| `features?` | `string[]` | Optional benefit rows under the subtitle. |
| `title?` | `string` | — |
| `subtitle?` | `string` | — |
| `className?` | `string` | — |
| `ctaLabel?` | `string` | @deprecated Use `plans[].cta`. Overrides every plan's CTA (and its in-place swap). |
| `monthlyPrice?` | `string` | @deprecated Use `plans`. |
| `yearlyPrice?` | `string` | @deprecated Use `plans`. |
| `monthlyNote?` | `string` | @deprecated Use `plans`. |
| `yearlyNote?` | `string` | @deprecated Use `plans`. |
| `monthlyPerMonth?` | `string` | @deprecated Use `plans`. |
| `yearlyPerMonth?` | `string` | @deprecated Use `plans`. |
| `yearlyBadge?` | `string` | @deprecated Use `plans`. |

## Icons rendered

- `zap` — /icons.html?i=zap
- `bell` — /icons.html?i=bell
- `credit-card` — /icons.html?i=credit-card
- `download` — /icons.html?i=download
- `shield` — /icons.html?i=shield
- `refresh` — /icons.html?i=refresh
- `check` — /icons.html?i=check
- `x` — /icons.html?i=x

Source: https://lumioui.com/components/paywall-sheet