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

In React, wrap your app in SuperwallProvider once. In plain JavaScript, call createSuperwall. Both return an instance synchronously and start configuring in the background.

import { SuperwallProvider } from "@superwall/paywalls-react";

export function Root() {
  return (
    <SuperwallProvider apiKey="pk_…">
      <App />
    </SuperwallProvider>
  );
}
import { createSuperwall } from "@superwall/paywalls-js";

const sw = createSuperwall({ apiKey: "pk_…" });

Do this once, as early as your app boots. In React the provider holds the instance for you; reach it from any component with useSuperwall(). In plain JavaScript, 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.

import { use } from "react";
import { useSuperwall, usePlacement } from "@superwall/paywalls-react";

function Checkout() {
  const sw = useSuperwall();
  use(sw.ready); // suspends until configured

  const { register } = usePlacement();
  return <button onClick={() => register({ placement: "checkout" })}>Upgrade</button>;
}
const sw = createSuperwall({ apiKey: "pk_…" });

await sw.ready;

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

In React, use(sw.ready) suspends the component, so put a <Suspense> boundary above it. Gating render on configuration covers the boundary and its error handling.

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".

import { useSuperwall, useSignal } from "@superwall/paywalls-react";

function ConfigGuard({ children }: { children: React.ReactNode }) {
  const sw = useSuperwall();
  const status = useSignal(sw.configurationStatus);

  if (status === "failed") {
    // Superwall could not configure. Paywalls will not present.
    return null;
  }
  return children;
}
await sw.ready;

if (sw.configurationStatus.value === "failed") {
  // Superwall could not configure. Paywalls will not present.
}

Options

SuperwallProvider accepts every option createSuperwall does, as props. The two are the same configuration surface.

<SuperwallProvider
  apiKey="pk_…"
  options={{
    /* SuperwallOptions: logging, networking, paywall behavior */
  }}
  delegate={myDelegate}
  storage={myStorageAdapter}
  purchaseController={myPurchaseController}
  identity={{
    appUserId: "user_123",
    aliasId: "$SuperwallAlias:…",
    vendorId: "…",
    vendorIdProvider: async () => "…",
  }}
>
  <App />
</SuperwallProvider>
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 () => "…",
  },
});
OptionPurpose
apiKeyYour pk_… publishable key. Required.
optionsTuning for logging, networking, and paywall behavior.
delegateGlobal callbacks for SDK-wide events. See Events.
storageCustom storage adapter. Defaults to localStorage plus cookies in the browser.
purchaseControllerTake over checkout. Omit to use the built-in Stripe flow. See Purchases.
identityPre-seed identity. Useful on the server or during SSR hydration.
surveyPresenterRenderer 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.

In React, the provider reads these props once, on first mount for a given apiKey. Changing them afterwards does not reconfigure the SDK. See Configuration is read once.

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.

React apps do not need this. SuperwallProvider already holds the instance, and the hooks reach it through context.

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?

On this page