cachingEnabled is false (there would be nothing to retain).
Prefetch on demand
prefetch(placementIds) warms each placement’s flow JSON (memory + disk cache) and custom fonts by running the same resolve path a present would. A later presentPlacement for a warmed placement then hits the cache with no network. It runs all placements concurrently and never rejects: it resolves to a Record<string, PrefetchOutcome> so you can see exactly what is warm.
cachingEnabled is false, the call is skipped and returns {}.
Warming images too
By defaultprefetch warms only JSON and fonts. Pass { warmMedia: true } to also warm images into the image cache so the flow paints instantly on arrival. The screen window is bounded by prefetchMediaStrategy (first screen by default).
PrefetchOutcome
Prefetch on launch
Declare placements to warm automatically, once, right afterconfigure(...). Warming runs in the background and never blocks startup. When 2+ placements are declared, they are resolved in a single batch round-trip (falling back to per-placement resolves if the batch endpoint is unavailable).
prefetchMediaStrategy, so it warms first-screen images by default (unlike a bare prefetch([...]), which warms no media unless you ask).
Warmed flows only live as long as their freshness TTL (the resolve response’s
cacheTtlSeconds, falling back to the environment default). The development environment uses a 0 TTL by default, so a warmed entry expires immediately and launch prefetch is effectively a no-op there. Use staging or production to see the benefit. See Caching.prefetchMediaStrategy
Governs how aggressively launch prefetch warms images, and bounds the screen window when an explicit prefetch(..., { warmMedia: true }) opts into media.
| Strategy | Warms |
|---|---|
none | Flow JSON + fonts only; no images. |
firstScreen (default) | Also the first screen’s and persistent-zone images. |
allScreens | Also every screen’s images (best for short flows). |
Readiness check
isPlacementReady(placementKey) answers whether a presentable flow is currently available for the current identity. It runs through the same cache-populating path as prefetch, so the resolve it may perform is not wasted: a presentable flow is left warm, and a following presentPlacement hits the cache (Tier 0) instead of resolving again. The ready-then-present sequence therefore costs at most one network resolve.
true when any tier can present (fresh cache, live resolve, stale cache, or bundled default) and false when the backend has nothing to show or every tier fails. It never throws.
Example: warm right after sign-in
A common pattern is to warm the next flow the moment the user signs in, so the first paywall opens instantly.Common mistakes
- Prefetching with caching disabled.
prefetchandprefetchOnLaunchare no-ops whencachingEnabledisfalse. Enable caching to use them. - Expecting launch prefetch to help in
development. The0default TTL expires the warm entry immediately. Test prefetch againststagingorproduction. - Awaiting
prefetchon a critical path. It is best-effort and can take a round-trip. Fire it in the background (void FlowPilot.prefetch(...)) and let the present hit whatever is warm. - Assuming
warmMediadefaults on. A bareprefetch([...])warms JSON and fonts only. Pass{ warmMedia: true }for images.
Troubleshooting
prefetchreturnsnoFlowfor a placement. The backend resolved the placement but has nothing to show (no published flow, or targeting excludes this user). Expected; not an error.- First present still shows a spinner after prefetch. The TTL may have expired (especially in
development), the identity changed since warming, orwarmMediawas off so images still load on arrival. ChecklogLevel: 'debug'output for the delivery source. isPlacementReadyreturnsfalseunexpectedly. Offline with no cache and no bundled default, or the backend has no flow for this user. Ship a bundled default for guaranteed readiness.