The SDK must be configured first. A present or resolve before
FlowPilot.configure(...) throws (imperative presentPlacement surfaces it as an error outcome). See Configuration.The presentation APIs at a glance
| API | Returns | On no flow | Use when |
|---|---|---|---|
presentPlacement(key) | Promise<FlowPresentationResult> | resolves with outcome: 'error' | You want a modal shown imperatively and the outcome back. |
createSession(key) | Promise<FlowSession> | rejects (throws) | You render the flow yourself with <FlowPilotPresenter />. |
resolveSession(key) | Promise<FlowSession | null> | resolves to null | Onboarding and other “must not fail” entry points. |
Imperative: present a modal
presentPlacement(placementId) resolves the placement, opens a full-screen modal, and resolves a promise once the user completes or dismisses the flow. It never rejects for predictable failures (network error, missing placement, not configured); it resolves with an error outcome instead, so a single switch covers every case.
presentPlacement takes only the placement key. There is no per-present options argument in this build (no additional context, no presentation-style override). To vary targeting, set context in configuration; to control the modal, use the declarative path below.
Declarative: render a session
For control over safe-area handling, status bar, or embedding a flow inside your own view tree, build aFlowSession with createSession and render it with <FlowPilotPresenter />.
createSession(placementId) fetches the placement (using the cache if available) and returns the session unstarted, so you can wire it up before the first screen shows. Call session.start() to begin. It throws when no presentable flow exists, so handle that (or use resolveSession, below).
<FlowPilotPresenter /> props
The component wraps the renderer in a full-screen React Native Modal. Pass session={null} to hide it.
The session to render.
null hides the presenter.Called once when the flow completes or is dismissed. Set
session back to null here to close the modal.Safe-area insets, normally from
useSafeAreaInsets(). The renderer lays out around the notch and home indicator with these.The modal’s present/dismiss animation (passed straight to React Native’s
Modal).The iOS modal presentation style (passed to
Modal). Ignored on Android.Status bar style applied while the flow is presented.
Host UI rendered instead of the loading spinner if the presentation fails before any screen shows. See Host fallback.
Called once if the presentation enters the error state.
For advanced embedding without a modal, the lower-level
FlowPresenter component (and its FlowPresenterProps) is also exported. FlowPilotPresenter is FlowPresenter wrapped in a Modal; reach for FlowPresenter only when you need to host the flow inside your own container. Most apps use FlowPilotPresenter.Host fallback (must not fail)
For anything user-facing, like onboarding, make sure the user always sees something even when every fail-safe tier misses (offline, no cache, no bundled default).resolveSession(placementId) is the non-throwing resolver. It walks the full fail-safe chain and returns a ready FlowSession, or null when nothing is presentable, so you can render your own native UI instead.
fallback to the presenter. It renders instead of the loading spinner when a presentation fails before any screen shows (for example a navigation dead-end caught by the presentation watchdog):
Common mistakes
- Forgetting
session.start().createSessionreturns the session unstarted. Withoutstart(), the presenter shows nothing. (The imperativepresentPlacementstarts it for you.) - Not clearing the session on completion. With the declarative path, set
sessionback tonullinonComplete, or the modal stays up. - Not handling the throw from
createSession. It rejects when there is no presentable flow. Add a.catch, or useresolveSession, which returnsnullinstead. - Expecting a per-present context argument. There is none. Set
contextat configure time. - Rendering
FlowPilotPresenterwithout aSafeAreaProvider.useSafeAreaInsets()returns zeros, so the flow can render under the status bar. Wrap your app inSafeAreaProvider. See Installation.
Troubleshooting
The imperative call returnederror, or createSession threw / resolveSession returned null:
| Symptom | Likely cause | Fix |
|---|---|---|
error outcome, code FLOW_NOT_FOUND | No published flow attached to the placement, or it is paused | Attach and publish a flow, set the placement active. See Placements. |
| Nothing resolves, offline | Device offline with no cache and no bundled default | Ship a bundled default flow or pre-warm with prefetch. |
error outcome, code TIMEOUT | Resolve exceeded resolveTimeout (default 4s) and nothing was cached | The SDK already falls back to cache/bundled; raise resolveTimeout only if needed. |
| Resolves on one device, not another | Audience targeting excludes this user/context | Check the placement’s audience rules and the context you pass. |
| Always empty for one app | Wrong appId, the placement belongs to another app | Configure the appId that owns the placement. |