Configure
Create a Superwall instance and understand what happens before it is ready.
Beta
The Web SDK is in beta and its API may change between releases.
Create an instance
createSuperwall returns an instance synchronously and starts configuring in the background.
import { createSuperwall } from "@superwall/paywalls-js";
const sw = createSuperwall({ apiKey: "pk_…" });Do this once, as early as your app boots. Keep the returned instance around, or use the named exports and let the SDK hold it for you.
Wait for ready before presenting
register waits for identity to hydrate, but it does not wait for configuration. Called before config lands, it returns { type: "error" } carrying a PaywallNotAvailableError.
const sw = createSuperwall({ apiKey: "pk_…" });
await sw.ready;
const result = await sw.register({ placement: "checkout" });Reads never block. sw.user.id.value and sw.subscriptionStatus.value return synchronously at any time. Before hydration lands they return defaults ("" and { status: "UNKNOWN" }). Persisted state replaces the defaults shortly after.
Treat UNKNOWN as unresolved, not as unsubscribed. A returning subscriber reads UNKNOWN until
hydration completes, so branching on "not ACTIVE" at startup shows paying users an upsell.
Check configurationStatus, not ready, to detect failure. A failed config fetch is swallowed internally: sw.ready still resolves, and sw.configurationStatus becomes "failed".
await sw.ready;
if (sw.configurationStatus.value === "failed") {
// Superwall could not configure. Paywalls will not present.
}Options
const sw = createSuperwall({
apiKey: "pk_…",
options: {
/* SuperwallOptions: logging, networking, paywall behavior */
},
delegate: myDelegate,
storage: myStorageAdapter,
purchaseController: myPurchaseController,
identity: {
appUserId: "user_123",
aliasId: "$SuperwallAlias:…",
vendorId: "…",
vendorIdProvider: async () => "…",
},
});| Option | Purpose |
|---|---|
apiKey | Your pk_… publishable key. Required. |
options | Tuning for logging, networking, and paywall behavior. |
delegate | Global callbacks for SDK-wide events. See Events. |
storage | Custom storage adapter. Defaults to localStorage plus cookies in the browser. |
purchaseController | Take over checkout. Omit to use the built-in Stripe flow. See Purchases. |
identity | Pre-seed identity. Useful on the server or during SSR hydration. |
surveyPresenter | Renderer for post-paywall surveys. Omit and the SDK skips survey presentation. |
identity.vendorIdProvider is where a fingerprinting library plugs in. The SDK does not bundle
fingerprinting.
Named exports
Instead of threading the instance through your app, import the namespaces directly. The first createSuperwall call registers the default instance, and these bind to it.
import { createSuperwall, user, register, events } from "@superwall/paywalls-js";
createSuperwall({ apiKey: "pk_…" });
await user.identify("user_42");
const result = await register({ placement: "checkout" });Importing only user lets bundlers drop the rest as dead code.
Multiple instances
Creating more than one instance is supported. Useful for tests, Storybook, and multi-tenant edge workers. The default instance is the first one created in the process, and it is the one the named exports target.
Next, present your first paywall.
How is this guide?