Provider

Wire the Superwall Web SDK into a React 19 app.

Beta

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

Wrap your app

SuperwallProvider creates and holds the instance. It takes the same options as createSuperwall, plus children.

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

function App() {
  return (
    <SuperwallProvider apiKey="pk_…">
      <Home />
    </SuperwallProvider>
  );
}

Everything below it can reach the instance through the hooks.

Instances are cached by apiKey in a module-level registry. Mounting two providers with the same key reuses one instance, and the instance survives Fast Refresh. The registry is never evicted: swapping apiKey leaves the old instance configured and running for the page's lifetime.

Configuration is read once

The provider reads its options on the first mount for a given apiKey. A later provider with the same key and different options silently reuses the original instance's configuration. Changing other props afterwards does not reconfigure the SDK. Changing apiKey swaps to a different instance.

Pass configuration statically:

<SuperwallProvider
  apiKey="pk_…"
  identity={{ appUserId: currentUser?.id }}
  delegate={myDelegate}
>
  <Home />
</SuperwallProvider>

If identity is not known at mount, leave it out and call identify from useUser once you have it.

Gating render on configuration

Registering a placement before configuration lands fails, so gate the parts of your UI that present paywalls. One way is a Suspense boundary over sw.ready:

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

function ConfigGate({ children }: { children: React.ReactNode }) {
  const sw = useSuperwall();
  use(sw.ready);
  return <>{children}</>;
}

function App() {
  return (
    <SuperwallProvider apiKey="pk_…">
      <ErrorBoundary fallback={<Offline />}>
        <Suspense fallback={<Splash />}>
          <ConfigGate>
            <Home />
          </ConfigGate>
        </Suspense>
      </ErrorBoundary>
    </SuperwallProvider>
  );
}

sw.ready can reject, and use() rethrows. Without an error boundary the subtree unmounts. A failed config fetch does not reject ready; check sw.configurationStatus.value === "failed" for that. Do not gate with use(sw.ready) during server rendering.

Server rendering

@superwall/paywalls-react is safe to import during SSR. Its entry pulls in the core package's /browser module, but nothing on that path touches the DOM at module load. Every access is inside a function and guarded. Paywall presentation happens after hydration.

The React package re-exports the entire public surface of @superwall/paywalls-js, so you never need to import both.

Next, the hooks.

How is this guide?

On this page