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. 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);
}

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),
  },
});

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