Present your first paywall

Register a placement and show a paywall in the browser.

Beta

The Web SDK is in beta and its API may change between releases.

Register a placement

register is the primary entry point. It mirrors Superwall.shared.register(...) on iOS and Android. You give it a placement name; the SDK evaluates your campaign's audience rules and shows a paywall if the user matches one.

const result = await sw.register({ placement: "checkout" });

The placement must exist in a campaign in your Superwall dashboard, added as an in-app placement. If nothing matches, nothing is shown. That is not an error.

What you get back

type RegisterPlacementResult =
  | { type: "presented"; info: PaywallInfo; result: PaywallResult }
  | { type: "skipped"; reason: PaywallSkippedReason }
  | { type: "error"; error: Error };

Skipped reasons and errors surface in the return value rather than throwing.

const result = await sw.register({ placement: "checkout" });

if (result.type === "presented" && result.result.type === "purchased") {
  console.log("Bought:", result.result.productId);
}

A purchase made through the paywall's web checkout also carries result.result.checkout, with any redemption codes (0.3.1 or later), redirect URL, and deep links for that purchase. See The checkout payload.

Run code when the user is entitled

Pass a feature callback and the SDK runs it when the user should get access.

await sw.register({
  placement: "checkout",
  feature: () => unlockExportButton(),
});

See Feature gating for exactly when it runs.

Per-call callbacks

handler gives you lifecycle callbacks scoped to this one call.

await sw.register({
  placement: "checkout",
  handler: {
    onPresent: (info) => console.log("opened", info.identifier),
    onDismiss: (info, result) => console.log("dismissed", result),
    onSkip: (reason) => console.log("skipped", reason),
    onError: (error) => console.error(error),
  },
});

To change what happens after a web checkout, such as sending the buyer to your mobile app, add onPurchase. It replaces the SDK's default post-purchase handling. See After a purchase.

Passing parameters

params are the values your campaign's audience rules filter on.

await sw.register({
  placement: "checkout",
  params: { plan: "team", seats: 12 },
});

Overriding presentation

Three more optional arguments change how the paywall is shown for a single call:

ArgumentEffect
overridesPer-call presentation tweaks applied before the iframe mounts. Currently presentationStyle.
paywallRender your own UI instead of the default iframe. The SDK still runs the full pipeline and fires the same events.
presenterReplace the presenter entirely for this call. Highest precedence.

Precedence is presenter > paywall > the default browser presenter. In React, useCustomPaywall wraps the paywall path.

Preloading

Paywalls load in an iframe. During configuration the SDK preloads up to six paywalls that have a URL, two at a time.

sw.placements.preloadAll() and preloadFor() currently do nothing and are being removed from the API. Do not build on them.

One paywall at a time

Only one paywall can be on screen at once. Calling register while another is presented fails with PaywallAlreadyPresentedError. To close the current one yourself:

sw.dismiss();

sw.isPaywallPresented is a reactive read of whether one is currently up. sw.activePaywall carries the presented paywall's info.

Next, identify your users.

How is this guide?

On this page