Skip to main content
Preloading paywalls reduces latency when presenting them to users by downloading and caching paywall content in advance.

Why Preload?

Preloading paywalls helps you:
  • Reduce presentation delay - Paywalls show instantly without loading
  • Improve user experience - No loading spinners or delays
  • Increase conversion - Faster paywalls lead to higher conversion rates
  • Handle poor connectivity - Paywalls work even with slow networks
Preloading downloads paywall assets and product information. This uses bandwidth and storage. Only preload paywalls you expect to show soon.

Preloading All Paywalls

Preload all configured paywalls at once:
import { useSuperwall } from "expo-superwall";
import { useEffect } from "react";

function App() {
  const { preloadAllPaywalls } = useSuperwall();

  useEffect(() => {
    // Preload all paywalls on app launch
    preloadAllPaywalls();
  }, []);

  return <YourApp />;
}

Automatic Preloading

Enable automatic preloading in the provider configuration:
import { SuperwallProvider } from "expo-superwall";

export default function App() {
  return (
    <SuperwallProvider
      apiKeys={{ ios: "YOUR_API_KEY" }}
      options={{
        paywalls: {
          shouldPreload: true,  // Automatically preload all paywalls
        },
      }}
    >
      <YourApp />
    </SuperwallProvider>
  );
}
When shouldPreload is true, the SDK automatically preloads all paywalls after configuration completes.

Preloading Specific Paywalls

Preload only the paywalls you expect to show:
import { useSuperwall } from "expo-superwall";
import { useEffect } from "react";

function App() {
  const { preloadPaywalls } = useSuperwall();

  useEffect(() => {
    // Preload specific placements
    preloadPaywalls([
      "onboarding",
      "premium_feature",
      "remove_ads",
    ]);
  }, []);

  return <YourApp />;
}

Strategic Preloading

Preload paywalls based on user journey:
import { useSuperwall } from "expo-superwall";
import { useEffect } from "react";

function UserJourneyPreloader() {
  const { preloadPaywalls } = useSuperwall();

  useEffect(() => {
    // Preload onboarding paywall immediately
    preloadPaywalls(["onboarding"]);
  }, []);

  const preloadFeaturePaywalls = () => {
    // Preload feature paywalls when user explores features
    preloadPaywalls([
      "export_pdf",
      "advanced_filters",
      "cloud_sync",
    ]);
  };

  const preloadUpgradePaywall = () => {
    // Preload when user shows upgrade intent
    preloadPaywalls(["premium_upgrade"]);
  };

  return {
    preloadFeaturePaywalls,
    preloadUpgradePaywall,
  };
}

Preloading Timing

1

On App Launch

Preload critical paywalls immediately:
useEffect(() => {
  preloadPaywalls(["onboarding"]);
}, []);
2

After Onboarding

Preload feature paywalls after onboarding completes:
const handleOnboardingComplete = async () => {
  // User finished onboarding
  await completeOnboarding();
  
  // Preload feature paywalls
  preloadPaywalls(["premium_features", "remove_ads"]);
};
3

Before Feature Access

Preload before user attempts to use premium features:
function FeatureList() {
  const { preloadPaywalls } = useSuperwall();

  const handleFeatureHover = (feature: string) => {
    // Preload when user hovers over premium feature
    if (feature === "advanced_export") {
      preloadPaywalls(["export_paywall"]);
    }
  };

  return <FeatureGrid onFeatureHover={handleFeatureHover} />;
}

Best Practices

Prioritize paywalls users are most likely to see:
// ✅ Good - preload most important placements
useEffect(() => {
  preloadPaywalls([
    "onboarding",        // Shown to all new users
    "export",            // Most used feature
    "remove_ads",        // High conversion
  ]);
}, []);
Preloading consumes bandwidth and storage. Be selective:
// ✅ Good - preload 2-3 important paywalls
preloadPaywalls(["onboarding", "premium"]);

// ❌ Bad - preloading 10+ paywalls wastes resources
preloadPaywalls(["p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8"]);
Wait until app is fully loaded:
useEffect(() => {
  // Wait for critical data to load
  Promise.all([
    loadUserData(),
    loadAppConfig(),
  ]).then(() => {
    // Now preload paywalls
    preloadPaywalls(["premium"]);
  });
}, []);
Only preload on WiFi for large paywalls:
import NetInfo from "@react-native-community/netinfo";

useEffect(() => {
  NetInfo.fetch().then((state) => {
    // Only preload on WiFi
    if (state.type === "wifi") {
      preloadAllPaywalls();
    } else {
      // Preload only critical paywall on cellular
      preloadPaywalls(["onboarding"]);
    }
  });
}, []);

Preloading Strategies

Preload all paywalls immediately for instant presentation:
function App() {
  const { preloadAllPaywalls } = useSuperwall();

  useEffect(() => {
    preloadAllPaywalls();
  }, []);

  return <YourApp />;
}
Pros: Zero latency for paywall presentation Cons: Uses more bandwidth and storage

Complete Example

Here’s a comprehensive preloading setup:
import { SuperwallProvider, useSuperwall, useUser } from "expo-superwall";
import { useEffect } from "react";
import NetInfo from "@react-native-community/netinfo";

export default function App() {
  return (
    <SuperwallProvider
      apiKeys={{ ios: "YOUR_API_KEY" }}
      options={{
        paywalls: {
          shouldPreload: false,  // We'll handle preloading manually
        },
      }}
    >
      <PaywallPreloader />
      <YourApp />
    </SuperwallProvider>
  );
}

function PaywallPreloader() {
  const { preloadPaywalls } = useSuperwall();
  const { user } = useUser();

  useEffect(() => {
    initializePreloading();
  }, []);

  useEffect(() => {
    // Preload based on user state
    if (user) {
      preloadForUser(user);
    }
  }, [user]);

  const initializePreloading = async () => {
    try {
      // Check network type
      const netInfo = await NetInfo.fetch();
      
      if (netInfo.type === "wifi") {
        // On WiFi - preload multiple paywalls
        console.log("WiFi detected - preloading all critical paywalls");
        await preloadPaywalls([
          "onboarding",
          "premium_features",
          "remove_ads",
        ]);
      } else if (netInfo.type === "cellular") {
        // On cellular - only preload critical paywall
        console.log("Cellular detected - preloading essential paywall only");
        await preloadPaywalls(["onboarding"]);
      }
    } catch (error) {
      console.error("Failed to preload paywalls:", error);
    }
  };

  const preloadForUser = async (user: any) => {
    // Preload based on user attributes
    const placements: string[] = [];

    // Free users
    if (!user.subscriptionStatus || user.subscriptionStatus === "INACTIVE") {
      placements.push("upgrade", "trial_offer");
    }

    // Power users
    if (user.documentsCreated > 10) {
      placements.push("pro_features", "advanced_export");
    }

    // Users who completed onboarding
    if (user.onboardingCompleted) {
      placements.push("feature_discovery");
    }

    if (placements.length > 0) {
      console.log("Preloading placements for user:", placements);
      await preloadPaywalls(placements);
    }
  };

  return null;
}

function YourApp() {
  return (
    <View style={{ flex: 1 }}>
      {/* Your app content */}
    </View>
  );
}

Monitoring Preload Performance

Track preloading effectiveness:
import { useSuperwallEvents } from "expo-superwall";

function PreloadMonitor() {
  useSuperwallEvents({
    onPaywallPresent: (info) => {
      // Track how long it took to show
      const loadTime = info.webViewLoadDuration || 0;
      
      analytics.track("paywall_presented", {
        paywall_id: info.identifier,
        load_time_ms: loadTime,
        was_preloaded: loadTime < 100, // Estimate
      });
    },
  });

  return null;
}

Configuration vs Manual Preloading

<SuperwallProvider
  apiKeys={{ ios: "YOUR_API_KEY" }}
  options={{
    paywalls: {
      shouldPreload: true,
    },
  }}
>
  <YourApp />
</SuperwallProvider>
✅ Simple setup ✅ Works immediately ❌ Preloads all paywalls ❌ No control over timing

Troubleshooting

Ensure you’re preloading the correct placement:
// ✅ Correct - exact placement name
preloadPaywalls(["premium_feature"]);
registerPlacement({ placement: "premium_feature" });

// ❌ Wrong - mismatched names
preloadPaywalls(["premium"]);
registerPlacement({ placement: "premium_feature" });
Check that SDK is configured before preloading:
const { preloadPaywalls, isConfigured } = useSuperwall();

useEffect(() => {
  if (isConfigured) {
    preloadPaywalls(["premium"]);
  }
}, [isConfigured]);
Preload fewer paywalls at once:
// Instead of preloading all
// preloadAllPaywalls();

// Preload selectively
preloadPaywalls(["onboarding", "premium"]);

Next Steps

Displaying Paywalls

Learn how to present preloaded paywalls

Provider Setup

Configure automatic preloading options

Build docs developers (and LLMs) love