Quickstart
Scaffold a Superwall Framework project, preview your first paywall in the studio, and ship it to production.
This guide takes you from nothing to a live paywall: scaffold a project inside your app, preview it locally, and push it to Superwall.
Before you start
You'll need:
- Node 20+ (or Bun) and git.
- A Superwall account with an application. The application must have the headless paywalls feature enabled — a push will tell you if it isn't.
- The Superwall CLI:
bun add -g superwallnpm install -g superwall- 1
Create the project
From the root of your app's repo:
superwall createThis scaffolds a self-contained
superwall/directory — its ownpackage.json, a starter paywall, and everything wired up — then connects it to your Superwall app and installs dependencies. Your app itself needs no npm setup.To start from a working pattern instead, scaffold any example — each is a complete project:
superwall create --example multi-page - 2
Preview in the studio
superwall devThis opens the studio at
http://localhost:6100: every paywall as a card with a live preview, and an editor per paywall with a device-frame view at exact logical size. Switch devices, toggle light and dark, rotate, change locales, and simulate purchases — the studio asks you to pick each outcome, so you can test every branch of your flow. See The studio for the full tour.Edits hot-reload as you save. Warnings about project problems (a stray file in
app/, a duplicate route) appear here too — they're the same checks that block a push, so fix them as they come up. - 3
Make it yours
Open
superwall/paywalls/<id>/and edit. A paywall is ordinary React:// app/index.tsx import { useProducts, usePurchase, useActions, useHaptics } from "superwall/hooks"; export default function Paywall() { const { getProduct } = useProducts(); const { purchase } = usePurchase(); const { close } = useActions(); const haptics = useHaptics(); const annual = getProduct("annual"); return ( <main> <button aria-label="Close" onClick={() => { haptics.light(); close(); }}>×</button> <h1>Go Pro</h1> <button onClick={async () => { haptics.light(); const result = await purchase("annual"); if (result.status === "completed") haptics.success(); }} > {annual?.variables.price ? `Subscribe · ${annual.variables.price}` : "Subscribe"} </button> </main> ); }Two habits worth forming on day one:
- Guard every product read. Prices arrive from the store at runtime; in dev they're
undefineduntil the studio injects your dashboard's products. Degrade the copy — never invent a number. See Products. - Fire a haptic on every meaningful tap. iOS gives no feedback of its own inside a paywall. See Styling & mobile design.
- Guard every product read. Prices arrive from the store at runtime; in dev they're
- 4
Point at real products
config.tsdeclares product slots — the key is the name your code uses, the value is the store identifier:import { definePaywall } from "superwall/config"; export default definePaywall({ name: "Pro — Annual", products: { annual: "pro_5999_year", }, });The identifiers must exist as products on your Superwall dashboard — a push refuses otherwise. Create them in the dashboard, or from the CLI with
superwall products create. See Products. - 5
Push, then promote
superwall push # build + seal an immutable version — production untouched superwall promote # point production at the latest pushPush saves, promote ships — the same split as git push and a deploy.
superwall publishdoes both in one step. The first push binds each paywall to your dashboard and records the binding insuperwall.lock; commit that file so every machine and CI push to the same paywalls. See Push, promote & publish. - 6
Show it in your app
Where to next
Project structure
The full directory layout, the two generated files, and what to commit.
Pages & navigation
Turn one page into a multi-step flow.
Purchases
Handle completed, abandoned, and failed — and why the buy button never shows a spinner.
Examples
Complete projects for product selection, onboarding quizzes, trials, and more.
How is this guide?