Skip to main content

Hooks API vs Compat SDK

The Expo Superwall SDK offers two distinct integration approaches to meet different project needs:
  1. Hooks API - Modern React-first approach (recommended for new projects)
  2. Compat SDK - Legacy compatibility layer for migrating from React Native Superwall SDK

Quick Comparison

FeatureHooks APICompat SDK
Importimport { usePlacement } from "expo-superwall"import Superwall from "expo-superwall/compat"
ArchitectureReact Context + Hooks + ZustandClass-based singleton
State ManagementReactive hooks with automatic re-rendersManual state management
Type SafetyFull TypeScript inferenceClass-based types
Best ForNew Expo projectsMigrating from React Native SDK
Learning CurveFamiliar for React developersFamiliar for Swift/Kotlin SDK users

Overview

The modern Hooks API leverages React’s component model with context, hooks, and reactive state management powered by Zustand.

Key Features

  • React-first design - Built for React Native best practices
  • Automatic state updates - Components re-render when subscription status or user changes
  • Type-safe - Full TypeScript support with inference
  • Composable - Mix and match hooks as needed
  • Declarative - Loading states, errors, and success handled through components

Example Usage

import { SuperwallProvider, usePlacement, useUser } from "expo-superwall";
import { Button, View } from "react-native";

function App() {
  return (
    <SuperwallProvider apiKeys={{ ios: "YOUR_API_KEY" }}>
      <FeatureScreen />
    </SuperwallProvider>
  );
}

function FeatureScreen() {
  const { registerPlacement } = usePlacement({
    onPresent: (info) => console.log("Paywall shown:", info.name),
    onDismiss: (info, result) => console.log("Result:", result.type),
  });

  const { subscriptionStatus, user } = useUser();

  const unlockFeature = async () => {
    await registerPlacement({
      placement: "premium_feature",
      feature: () => {
        // This runs if user has access
        console.log("Feature unlocked!");
      },
    });
  };

  return (
    <View>
      <Button title="Unlock Feature" onPress={unlockFeature} />
      <Text>Status: {subscriptionStatus?.status}</Text>
    </View>
  );
}

Available Hooks

  • useSuperwall() - Core store access
  • useUser() - User management and subscription status
  • usePlacement() - Paywall registration and presentation
  • useSuperwallEvents() - Low-level event subscription

Declarative Components

<SuperwallProvider apiKeys={{ ios: API_KEY }}>
  <SuperwallLoading>
    <ActivityIndicator />
  </SuperwallLoading>

  <SuperwallError>
    {(error) => <ErrorScreen error={error} />}
  </SuperwallError>

  <SuperwallLoaded>
    <YourApp />
  </SuperwallLoaded>
</SuperwallProvider>

Compat SDK

Overview

The Compat SDK provides a class-based interface matching the API surface of the legacy React Native Superwall SDK and native iOS/Android SDKs.

When to Use

  • Migrating from react-native-superwall to Expo SDK 53+
  • Team is familiar with the Swift/Kotlin Superwall SDKs
  • Existing codebase using the class-based pattern
  • Need feature parity with native SDKs

Example Usage

import Superwall from "expo-superwall/compat";
import { useEffect } from "react";
import { Platform, Button } from "react-native";

function App() {
  useEffect(() => {
    const apiKey = Platform.OS === "ios"
      ? "YOUR_IOS_KEY"
      : "YOUR_ANDROID_KEY";

    Superwall.configure({ apiKey });
  }, []);

  const unlockFeature = async () => {
    await Superwall.shared.register({
      placement: "premium_feature",
      feature() {
        console.log("Feature unlocked!");
      },
    });
  };

  return <Button title="Unlock Feature" onPress={unlockFeature} />;
}

Delegate Pattern

import Superwall, {
  SuperwallDelegate,
  EventType,
} from "expo-superwall/compat";

class MyDelegate extends SuperwallDelegate {
  handleSuperwallEvent(eventInfo) {
    switch (eventInfo.event.type) {
      case EventType.paywallOpen:
        console.log("Paywall opened");
        break;
      case EventType.paywallClose:
        console.log("Paywall closed");
        break;
      case EventType.transactionComplete:
        console.log("Purchase completed!");
        break;
    }
  }

  subscriptionStatusDidChange(from, to) {
    console.log(`Subscription changed: ${from} -> ${to}`);
  }
}

const delegate = new MyDelegate();
await Superwall.shared.setDelegate(delegate);

Migration Path

From Compat to Hooks

If you started with the Compat SDK but want to adopt the modern Hooks API:

1. Replace Configuration

Before (Compat):
Superwall.configure({
  apiKey: "YOUR_API_KEY",
  options: new SuperwallOptions({
    paywalls: { shouldPreload: true }
  })
});
After (Hooks):
<SuperwallProvider
  apiKeys={{ ios: "YOUR_API_KEY" }}
  options={{ paywalls: { shouldPreload: true } }}
>
  {/* app */}
</SuperwallProvider>

2. Replace User Management

Before (Compat):
await Superwall.shared.identify({ userId: "user_123" });
await Superwall.shared.setUserAttributes({ tier: "premium" });
const status = await Superwall.shared.getSubscriptionStatus();
After (Hooks):
const { identify, update, subscriptionStatus } = useUser();

await identify("user_123");
await update({ tier: "premium" });
// subscriptionStatus is reactive - auto-updates
console.log(subscriptionStatus.status);

3. Replace Placement Registration

Before (Compat):
await Superwall.shared.register({
  placement: "feature_gate",
  handler: {
    onPresentHandler: (info) => console.log("Presented", info),
    onDismissHandler: (info, result) => console.log("Dismissed", result),
  },
  feature: () => console.log("Feature unlocked"),
});
After (Hooks):
const { registerPlacement } = usePlacement({
  onPresent: (info) => console.log("Presented", info),
  onDismiss: (info, result) => console.log("Dismissed", result),
});

await registerPlacement({
  placement: "feature_gate",
  feature: () => console.log("Feature unlocked"),
});

4. Replace Delegate with Events

Before (Compat):
class MyDelegate extends SuperwallDelegate {
  handleSuperwallEvent(eventInfo) {
    console.log(eventInfo.event.type);
  }
}
Superwall.shared.setDelegate(new MyDelegate());
After (Hooks):
import { useSuperwallEvents } from "expo-superwall";

function EventLogger() {
  useSuperwallEvents({
    onSuperwallEvent: (eventInfo) => {
      console.log(eventInfo.event.event);
    },
  });
  return null;
}

Type Definitions

Both APIs share the same underlying type system defined in SuperwallExpoModule.types.ts:
import type {
  PaywallInfo,
  PaywallResult,
  SubscriptionStatus,
  Entitlement,
} from "expo-superwall";

// These types work with both APIs
const info: PaywallInfo = { /* ... */ };
const status: SubscriptionStatus = { status: "ACTIVE", entitlements: [] };

Performance Considerations

Hooks API

  • Reactive updates - Components automatically re-render on state changes
  • Shallow equality - Uses zustand/shallow to minimize re-renders
  • Selective subscriptions - Only subscribe to state you need
// Only re-renders when subscription status changes
const { subscriptionStatus } = useSuperwall(state => ({
  subscriptionStatus: state.subscriptionStatus
}));

Compat SDK

  • Manual updates - You control when to fetch fresh data
  • No automatic re-renders - Use local state or external state management
  • Direct method calls - Lower overhead for one-off operations

Recommendation

For new projects: Use the Hooks API. It provides better React integration, automatic state management, and a more modern developer experience.
For migrations: Use the Compat SDK initially for faster migration, then gradually adopt hooks as you refactor components.

Further Reading

Build docs developers (and LLMs) love