Anatomy of a Paywall
How Paywalls Are Rendered
Paywalls are bundled into a single web file and rendered inside a native WebView:- Your app’s SDK receives a trigger and resolves it to a workflow.
- The SDK loads this bundle in a WKWebView (iOS) or WebView (Android).
- The paywall communicates back to the native app via a bridge — handling subscribe, dismiss, restore purchases, and product selection events.
Creating a Paywall
Paywalls can be created in four ways:
All creation methods follow the same flow:
- Enter a name and description.
- Select product buckets (and choose initial iOS/Android/Stripe products).
- Helium generates the initial files and opens the editor.
The Paywall Editor
The editor is a live coding environment powered by Sandpack (CodeSandbox’s embeddable bundler). You edit React code on the left and see a real-time preview on the right.Editable files
Auto-generated files (read-only in most cases)
Preview features
- Device selector — Preview on different phone models and sizes.
- Platform toggle — Switch between iOS and Android to see platform-specific products.
- Territory picker — Preview localized prices for any App Store or Play Store territory.
- Offer simulation — Toggle intro offer eligibility to preview trial and promotional pricing.
Versioning
Every paywall has a version history. Each save creates a new version, building a timeline of changes.Version statuses
How it works
- Every save (manual or autosave) creates a new draft version.
- The version stores all paywall files, product bucket references, font references, and a CRDT snapshot.
- Each version tracks its parent version, forming a version tree.
- You can browse version history and see who saved each version and when.
Saving
Paywalls support both manual and automatic saving:
Each save bundles the code into a deployable web file. This means your draft is always previewable — bundling doesn’t wait for publish.
Publishing
Publishing makes a draft version live. When you publish:- Validation — Helium checks for issues like hardcoded prices (prices should come from product data, not be typed into the UI).
- Status update — The draft becomes published. The previously published version becomes archived.
- SDK sync — The bundle_url on the paywall record is updated so the SDK fetches the new version.
- History entry — A record is added to paywall_publish_history with who published and when.
Products and Buckets
Paywalls don’t reference raw product IDs. Instead, they reference product buckets — cross-platform containers that hold iOS, Android, and Stripe products for a single logical offering.-
A paywall’s
product_bucket_idslists which buckets it uses. -
The editor resolves buckets into a
products.tsxfile with localized pricing for the selected territory. - When the SDK loads the paywall, it receives the product references and handles purchasing natively.
How Paywalls Connect to Workflows
Paywalls are used in workflows in three ways:- Control paywall — Every workflow has a default paywall shown when no targeting rules match.
- Audience targeting — A workflow rule can map an audience directly to a specific paywall.
- Experiment variants — Paywalls serve as variants in A/B tests, each receiving a share of traffic.
Second-Try Paywalls
A paywall can optionally have a second-try paywall — a follow-up paywall shown if the user dismisses the first one. This is configured via paywall settings field and allows for a softer re-engagement attempt (e.g., a discounted offer after the user declines the full-price paywall). Second-try paywalls can either appear after a paywall dismiss or after a subscription cancel event.Localization
Paywalls have a full localization system with per-locale translations, territory-aware pricing, and right-to-left layout support. Translation keys are defined and managed through the editor. Localizely support is available for localization management as well. See [Localization] for the complete localization documentation.Key Rules
- Paywall names can be up to 100 characters.
- Only published paywalls can be used in workflows and experiments.
- Only one published version exists per paywall at a time.
- Publishing runs validation — hardcoded prices and other issues can block publish.
- Paywalls cannot be deleted if they’re referenced by a workflow.
- Every save bundles the code — drafts are always previewable.
- Product data is auto-generated from buckets — don’t hardcode prices in your UI.
- Paywalls render in a WebView — they support standard web features (CSS, fonts, animations, Lottie).