Skip to main content
React hook for subscribing to a wide range of native Superwall events. This hook allows you to define callback functions for various events emitted by the Superwall SDK, such as paywall presentation, dismissal, subscription status changes, logging, and more. Event listeners are automatically added when the component mounts and removed when it unmounts. It must be used within a component that is a descendant of <SuperwallProvider />.

Function Signature

function useSuperwallEvents(
  callbacks?: SuperwallEventCallbacks
): void

Parameters

callbacks
SuperwallEventCallbacks
An object where keys are event names and values are the corresponding callback functions.

Paywall Lifecycle Callbacks

callbacks.onPaywallPresent
(paywallInfo: PaywallInfo) => void
Called when a paywall is presented.
callbacks.onPaywallDismiss
(paywallInfo: PaywallInfo, result: PaywallResult) => void
Called when a paywall is dismissed.
  • result.type can be: “purchased”, “declined”, or “restored”
callbacks.onPaywallSkip
(reason: PaywallSkippedReason) => void
Called when a paywall is skipped.
  • reason.type can be: “Holdout”, “NoAudienceMatch”, or “PlacementNotFound”
callbacks.onPaywallError
(error: string) => void
Called when an error occurs during paywall presentation or other SDK operations.
callbacks.willPresentPaywall
(paywallInfo: PaywallInfo) => void
Called just before a paywall is presented.
callbacks.didPresentPaywall
(paywallInfo: PaywallInfo) => void
Called after a paywall has been presented.
callbacks.willDismissPaywall
(paywallInfo: PaywallInfo) => void
Called just before a paywall is dismissed.
callbacks.didDismissPaywall
(paywallInfo: PaywallInfo) => void
Called after a paywall has been dismissed.

Subscription & User Callbacks

callbacks.onSubscriptionStatusChange
(status: SubscriptionStatus) => void
Called when the user’s subscription status changes.

Purchase Callbacks

callbacks.onPurchase
(params: any) => void
Called when a purchase is initiated.
  • iOS: includes productId and platform
  • Android: includes productId, platform, basePlanId, and offerId
callbacks.onPurchaseRestore
() => void
Called when a purchase restoration is initiated.
callbacks.onPaywallWillOpenURL
(url: string) => void
Called when the paywall attempts to open a URL.
Called when the paywall attempts to open a deep link.
Called before the SDK attempts to redeem a promotional link.
Called after the SDK has attempted to redeem a promotional link.
  • result.status can be: “SUCCESS”, “ERROR”, “CODE_EXPIRED”, “INVALID_CODE”, or “EXPIRED_SUBSCRIPTION”

General Event Callbacks

callbacks.onSuperwallEvent
(eventInfo: SuperwallEventInfo) => void
Called for all Superwall internal events. This is a general-purpose event handler that receives all SDK events.
  • Use this for comprehensive event logging or analytics
callbacks.onCustomPaywallAction
(name: string) => void
Called when a custom action is triggered from a paywall (e.g., via superwall.triggerCustomPaywallAction('myAction')).
callbacks.onCustomCallback
(callback: CustomCallback) => Promise<CustomCallbackResult> | CustomCallbackResult
Called when a custom callback is invoked from a paywall. Custom callbacks allow paywalls to communicate with the app to perform operations like validation, data fetching, etc.Parameters:
  • callback.name: string - The callback name
  • callback.variables?: Record<string, any> - Optional variables from paywall
Must return:
  • status: "success" | "failure" - Whether the callback succeeded
  • data?: Record<string, any> - Optional data to return to paywall

Logging Callbacks

callbacks.onLog
(params: LogParams) => void
Called for logging messages from the SDK.LogParams:
  • level: LogLevel - “debug”, “info”, “warn”, “error”
  • scope: LogScope - The scope/category of the log
  • message: string | null - The log message
  • info: Record<string, any> | null - Additional info
  • error: string | null - Error message if applicable

Internal Parameters

callbacks.handlerId
string
An optional identifier used to scope certain events (like onPaywallPresent, onPaywallDismiss, onPaywallSkip) to a specific usePlacement hook instance. This is primarily used internally by the usePlacement hook.
callbacks.onBackPressed
(paywallInfo: PaywallInfo) => boolean
Android only. Called when the back button is pressed while a paywall is displayed. Only triggered when rerouteBackButton is enabled in the paywall settings.Returns:
  • true to consume the back press and prevent default dismiss behavior
  • false to allow normal dismiss

Return Value

This hook does not return any values. Its purpose is to set up and tear down event listeners.

Usage Examples

Basic event logging

import { useSuperwallEvents } from 'expo-superwall';

function EventLogger() {
  useSuperwallEvents({
    onPaywallPresent: (info) => {
      console.log('Paywall presented:', info.name);
    },
    onPaywallDismiss: (info, result) => {
      console.log('Paywall dismissed:', result.type);
      if (result.type === 'purchased') {
        console.log('Product purchased:', result.productId);
      }
    },
    onSubscriptionStatusChange: (status) => {
      console.log('Subscription status changed:', status.status);
    },
  });

  return null; // This component only sets up listeners
}

Comprehensive event tracking

import { useSuperwallEvents } from 'expo-superwall';

function AnalyticsTracker() {
  useSuperwallEvents({
    onSuperwallEvent: (eventInfo) => {
      // Track all Superwall events in your analytics
      analytics.track('Superwall Event', {
        event: eventInfo.event.event,
        params: eventInfo,
      });
    },
    onPaywallPresent: (info) => {
      analytics.track('Paywall Shown', {
        paywallId: info.identifier,
        paywallName: info.name,
        experimentId: info.experiment?.id,
      });
    },
    onPaywallDismiss: (info, result) => {
      analytics.track('Paywall Dismissed', {
        paywallId: info.identifier,
        resultType: result.type,
        productId: result.type === 'purchased' ? result.productId : null,
      });
    },
  });

  return null;
}

Error tracking

import { useSuperwallEvents } from 'expo-superwall';

function ErrorTracker() {
  useSuperwallEvents({
    onPaywallError: (error) => {
      // Log error to error tracking service
      Sentry.captureMessage(`Superwall Paywall Error: ${error}`, 'error');
    },
    onLog: (params) => {
      if (params.level === 'error') {
        Sentry.captureMessage(
          `Superwall SDK Error [${params.scope}]: ${params.message}`,
          'error'
        );
      }
    },
  });

  return null;
}

Custom logging

import { useSuperwallEvents } from 'expo-superwall';

function CustomLogger() {
  useSuperwallEvents({
    onLog: ({ level, scope, message, info, error }) => {
      if (level === 'error' || level === 'warn') {
        console.log(`[Superwall ${level.toUpperCase()}] [${scope}]`);
        console.log('Message:', message);
        if (info) console.log('Info:', info);
        if (error) console.log('Error:', error);
      }
    },
  });

  return null;
}

Purchase flow tracking

import { useSuperwallEvents } from 'expo-superwall';
import { useState } from 'react';

function PurchaseFlowTracker() {
  const [isPurchasing, setIsPurchasing] = useState(false);

  useSuperwallEvents({
    onPurchase: (params) => {
      setIsPurchasing(true);
      console.log('Purchase started:', params.productId);
      analytics.track('Purchase Initiated', params);
    },
    onPaywallDismiss: (info, result) => {
      setIsPurchasing(false);
      
      if (result.type === 'purchased') {
        analytics.track('Purchase Completed', {
          productId: result.productId,
          paywallId: info.identifier,
        });
      }
    },
    onPurchaseRestore: () => {
      console.log('Purchase restoration started');
      analytics.track('Restore Purchases Initiated');
    },
  });

  return null;
}
import { useSuperwallEvents } from 'expo-superwall';
import { Linking } from 'react-native';

function DeepLinkHandler() {
  useSuperwallEvents({
    onPaywallWillOpenURL: (url) => {
      console.log('Opening URL:', url);
      // Custom URL handling logic
      Linking.openURL(url);
    },
    onPaywallWillOpenDeepLink: (url) => {
      console.log('Opening deep link:', url);
      // Custom deep link handling
      handleDeepLink(url);
    },
  });

  return null;
}

Promotional code redemption

import { useSuperwallEvents } from 'expo-superwall';
import { Alert } from 'react-native';

function PromoCodeHandler() {
  useSuperwallEvents({
    willRedeemLink: () => {
      console.log('Redeeming promotional code...');
    },
    didRedeemLink: (result) => {
      if (result.status === 'SUCCESS') {
        Alert.alert('Success', 'Promotional code redeemed!');
        console.log('Redemption info:', result.redemptionInfo);
      } else if (result.status === 'CODE_EXPIRED') {
        Alert.alert('Expired', 'This promotional code has expired.');
      } else if (result.status === 'INVALID_CODE') {
        Alert.alert('Invalid', 'This promotional code is invalid.');
      } else if (result.status === 'ERROR') {
        Alert.alert('Error', result.error.message);
      }
    },
  });

  return null;
}

Custom callback implementation

import { useSuperwallEvents } from 'expo-superwall';

function CustomCallbackHandler() {
  useSuperwallEvents({
    onCustomCallback: async (callback) => {
      console.log('Custom callback:', callback.name);
      console.log('Variables:', callback.variables);
      
      // Handle different callback types
      if (callback.name === 'validate_email') {
        const email = callback.variables?.email;
        const isValid = validateEmail(email);
        
        return {
          status: isValid ? 'success' : 'failure',
          data: {
            isValid,
            message: isValid ? 'Email is valid' : 'Invalid email format',
          },
        };
      }
      
      if (callback.name === 'fetch_user_data') {
        try {
          const userData = await fetchUserData();
          return {
            status: 'success',
            data: userData,
          };
        } catch (error) {
          return {
            status: 'failure',
            data: { error: error.message },
          };
        }
      }
      
      // Default: return failure for unknown callbacks
      return { status: 'failure' };
    },
  });

  return null;
}

Lifecycle event tracking

import { useSuperwallEvents } from 'expo-superwall';

function PaywallLifecycleTracker() {
  useSuperwallEvents({
    willPresentPaywall: (info) => {
      console.log('About to present:', info.name);
      analytics.track('Paywall Will Present', { paywallId: info.identifier });
    },
    didPresentPaywall: (info) => {
      console.log('Presented:', info.name);
      analytics.track('Paywall Did Present', { paywallId: info.identifier });
    },
    willDismissPaywall: (info) => {
      console.log('About to dismiss:', info.name);
      analytics.track('Paywall Will Dismiss', { paywallId: info.identifier });
    },
    didDismissPaywall: (info) => {
      console.log('Dismissed:', info.name);
      analytics.track('Paywall Did Dismiss', { paywallId: info.identifier });
    },
  });

  return null;
}

Best Practices

  1. Use for global event tracking: This hook is ideal for app-wide event logging, analytics, and monitoring
  2. Keep callbacks lightweight: Event callbacks should execute quickly to avoid blocking the UI
  3. Use usePlacement for placement-specific events: For paywall-specific logic, prefer usePlacement callbacks over this hook
  4. Avoid state updates in all event callbacks: Be selective about which events trigger state updates to prevent excessive re-renders
  5. Implement error handling: Always handle errors in async callbacks (like onCustomCallback)
  • SuperwallEventCallbacks (useSuperwallEvents.ts:23)
  • PaywallInfo (SuperwallExpoModule.types.ts:392)
  • PaywallResult (SuperwallExpoModule.types.ts:177)
  • PaywallSkippedReason (SuperwallExpoModule.types.ts:148)
  • SubscriptionStatus (SuperwallExpoModule.types.ts:205)
  • SuperwallEventInfo (SuperwallExpoModule.types.ts - see SuperwallEvent union type)
  • CustomCallback (SuperwallExpoModule.types.ts:499)
  • CustomCallbackResult (SuperwallExpoModule.types.ts:517)
  • RedemptionResult (SuperwallExpoModule.types.ts:769)
  • LogLevel and LogScope types in SuperwallExpoModule.types.ts

Build docs developers (and LLMs) love