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
On App Launch
Preload critical paywalls immediately: useEffect (() => {
preloadPaywalls ([ "onboarding" ]);
}, []);
After Onboarding
Preload feature paywalls after onboarding completes: const handleOnboardingComplete = async () => {
// User finished onboarding
await completeOnboarding ();
// Preload feature paywalls
preloadPaywalls ([ "premium_features" , "remove_ads" ]);
};
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
Preload high-traffic placements first
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
]);
}, []);
Don't preload too many paywalls
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" ]);
Preload after network-intensive operations
Wait until app is fully loaded: useEffect (() => {
// Wait for critical data to load
Promise . all ([
loadUserData (),
loadAppConfig (),
]). then (() => {
// Now preload paywalls
preloadPaywalls ([ "premium" ]);
});
}, []);
Check network connectivity
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
Aggressive
Balanced
Conservative
Smart
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 storagePreload likely paywalls based on user behavior: function App () {
const { preloadPaywalls } = useSuperwall ();
useEffect (() => {
// Preload high-probability paywalls
preloadPaywalls ([
"onboarding" ,
"feature_upgrade" ,
]);
}, []);
return < YourApp /> ;
}
Pros: Good balance of performance and resource usage
Cons: Some paywalls may still have slight delayOnly preload right before showing: function FeatureButton () {
const { preloadPaywalls , registerPlacement } = useSuperwall ();
const handlePress = async () => {
// Preload just before showing
await preloadPaywalls ([ "premium" ]);
// Then register placement
await registerPlacement ({ placement: "premium" });
};
return < Button title = "Unlock" onPress = { handlePress } /> ;
}
Pros: Minimal resource usage
Cons: User sees loading delayPreload based on user actions and analytics: function SmartPreloader () {
const { preloadPaywalls } = useSuperwall ();
const { user } = useUser ();
useEffect (() => {
// Free users - preload upgrade paywalls
if ( user ?. plan === "free" ) {
preloadPaywalls ([ "upgrade" , "trial" ]);
}
// Trial users - preload conversion paywalls
if ( user ?. plan === "trial" ) {
preloadPaywalls ([ "convert_to_paid" ]);
}
// Power users - preload premium features
if ( user ?. documentsCreated > 10 ) {
preloadPaywalls ([ "pro_features" ]);
}
}, [ user ]);
return null ;
}
Pros: Optimal resource usage with best UX
Cons: More complex implementation
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 >
);
}
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 const { preloadPaywalls } = useSuperwall ();
useEffect (() => {
preloadPaywalls ([ "onboarding" , "premium" ]);
}, []);
✅ Full control over what to preload
✅ Control over timing
✅ Can respond to user behavior
❌ Requires more code
Troubleshooting
Paywalls still show loading spinner
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