React paywall — Custom UI

Override class names, disable default styles, or replace the locked overlay with your own component tree.

Class slots

Use appearance.classNames — keys include gate, overlay, card, btnPrimary, walletPicker, agent, and more. Export defaultPaywallClassNames to extend defaults.

Unstyled mode

<PaywallProvider
  appearance={{
    unstyled: true,
    classNames: {
      overlay: "absolute inset-0 flex items-center justify-center bg-black/80",
      card: "max-w-md rounded-xl bg-neutral-900 p-8",
      btnPrimary: "btn btn-primary w-full",
    },
  }}
>

renderLockedOverlay

Full control of the paywall UI while keeping inspect / pay / wallet logic:

<PaywallGate
  resourceUrl={url}
  renderLockedOverlay={(s) => (
    <div className="my-modal">
      <h2>{s.title}</h2>
      <p>{s.displayPrice}</p>
      {s.error && <p role="alert">{s.error}</p>}
      {!s.walletAddress && (
        <button onClick={s.onConnect} disabled={s.busy}>Connect</button>
      )}
      <button onClick={s.onPay} disabled={s.busy}>Pay</button>
      {s.showWalletPicker &&
        s.pickerWallets.map((w) => (
          <button key={w.id} onClick={() => s.onWalletSelect(w.id)}>{w.name}</button>
        ))}
    </div>
  )}
/>

State type: PaywallLockedOverlayState (exported).

Headless

  • usePaywall() + PaywallOverlay
  • usePaidFetch(resourceUrl) for custom fetch flows

Appearance provider only

<PaywallAppearanceProvider appearance={{ theme: { accent: "#000" } }}>
  <WalletPicker wallets={w} onSelect={…} />
</PaywallAppearanceProvider>

See also Theming.