<FlowPilotPresenter /> component. On the Expo SDK you always present a flow declaratively: you resolve a FlowSession and render it through the presenter component. There is a non-throwing resolver (resolveSession) for must-not-fail entry points like onboarding, and a throwing variant (createSession) when you want to handle the no-flow case yourself.
The SDK must be configured first. A resolve before
FlowPilot.configure(...) throws. See Configuration.The presentation APIs at a glance
You render whichever session you get through
<FlowPilotPresenter session={...} />.
Present a flow
resolveSession(placementId) is the recommended entry point. It walks the full fail-safe chain (cache, network, bundled default) and returns a ready FlowSession, or null when nothing is presentable, without throwing. Render the session with <FlowPilotPresenter />, and render your own native UI when it is null.
resolveSession returns the session already resolved and started by the presenter when rendered. You do not call session.start() yourself with this pattern; the presenter drives the session. (If you build a session with createSession and render the lower-level FlowPresenter directly, you are responsible for start().)
<FlowPilotPresenter /> props
The component wraps the renderer in a full-screen React Native Modal. Pass session={null} to hide it.
FlowSession | null
required
The session to render.
null hides the presenter.(result: FlowPresentationResult) => void
Called once when the flow completes or is dismissed, with a
FlowPresentationResult ({ outcome, error? }). Set session back to null here to close the modal.{ top: number; bottom: number; left: number; right: number }
Safe-area insets, normally from
useSafeAreaInsets(). The renderer lays out around the notch and home indicator with these.'none' | 'slide' | 'fade'
default:"'slide'"
The modal’s present/dismiss animation (passed straight to React Native’s
Modal).'fullScreen' | 'pageSheet' | 'formSheet' | 'overFullScreen'
default:"'fullScreen'"
The iOS modal presentation style (passed to
Modal). Ignored on Android.'default' | 'light-content' | 'dark-content'
Status bar style applied while the flow is presented.
React.ReactNode | (() => React.ReactNode)
Host UI rendered instead of the loading spinner if the presentation fails before any screen shows. See Host fallback.
(error: Error) => void
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.createSession: the throwing variant
When you would rather handle the no-flow case with a try/catch than a null check, use createSession(placementId). It fetches the placement (using the cache if available) and returns the session unstarted, then throws when no presentable flow exists.
context in configuration; to control the modal, use the <FlowPilotPresenter /> props above.
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 already gives you this: it returns null instead of throwing, so you can render your own native UI:
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):
Deprecated: presentPlacement
FlowPilot.presentPlacement(key) is deprecated on the Expo SDK and does not render a flow. React Native cannot present UI without a mounted component, and the Expo SDK has no global presentation host, so the method logs a deprecation warning and resolves immediately with { outcome: 'error' } without presenting anything. (Older builds instead ran the session headlessly, emitting flow_start and the entry screen_view but painting nothing, and never resolved for an interactive flow; either way, it cannot show a flow.)
Do not call it. Use resolveSession (or createSession) plus <FlowPilotPresenter />, shown above. (On the iOS SDK, which has a real presenting view controller, imperative presentPlacement is supported.)
Common mistakes
- Calling
presentPlacementon Expo. It is deprecated and renders nothing. UseresolveSession+<FlowPilotPresenter />. - Not clearing the session on completion. Set
sessionback tonullinonComplete, or the modal stays up. - Using
createSessionon a must-not-fail path with nocatch. 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
createSession threw, resolveSession returned null, or the presenter entered its error state:
See Error handling for every error code.