<Flow> Component
The <Flow> component is the primary React component for rendering flows using the render props pattern.
Import
Section titled “Import”import { Flow } from "@useflow/react";Required props
Section titled “Required props”The flow definition returned by defineFlow().
flow: RuntimeFlowDefinition<FlowDefinition, TContext>children
Section titled “children”Render function that receives flow state and returns React elements.
children: (state: FlowState) => ReactNodeFlow State Object:
{ // State stepId: string; step: StepInfo; // Current step configuration object context: TContext; status: "active" | "completed";
// Navigation next: (target?: string, update?: ContextUpdate) => void; skip: (target?: string, update?: ContextUpdate) => void; back: () => void; reset: () => void;
// Context Management setContext: (update: ContextUpdate<TContext>) => void;
// Metadata steps: Record<string, StepInfo>; nextSteps: readonly string[] | undefined; path: readonly PathEntry[]; history: readonly HistoryEntry[]; startedAt: number; completedAt?: number;
// Persistence save: () => Promise<void>; restore: (state: FlowState) => void; isRestoring: boolean;
// Rendering renderStep: (elements: StepElements) => ReactElement;}Optional props
Section titled “Optional props”initialContext
Section titled “initialContext”Initial context state for the flow.
initialContext?: TContextDefault: {}
instanceId
Section titled “instanceId”Unique identifier for flow instances (useful for reusable flows).
instanceId?: stringpersister
Section titled “persister”Persister for saving/restoring flow state.
persister?: FlowPersistersaveMode
Section titled “saveMode”When to automatically save state.
saveMode?: "always" | "navigation" | "manual""navigation"(default) - Save on next/skip/back"always"- Save on every context update"manual"- Only save when callingsave()
saveDebounce
Section titled “saveDebounce”Debounce delay for save operations (milliseconds).
saveDebounce?: numberDefault: 300
loadingComponent
Section titled “loadingComponent”Component to show while restoring state.
loadingComponent?: ReactNodeCallback props
Section titled “Callback props”onComplete
Section titled “onComplete”Called when flow reaches a terminal step.
onComplete?: (event: { context: TContext }) => voidonNext
Section titled “onNext”Called on forward navigation.
onNext?: (event: { from: string; to: string; oldContext: TContext; newContext: TContext;}) => voidonSkip
Section titled “onSkip”Called when skipping a step.
onSkip?: (event: { from: string; to: string; oldContext: TContext; newContext: TContext;}) => voidonBack
Section titled “onBack”Called on backward navigation.
onBack?: (event: { from: string; to: string; oldContext: TContext; newContext: TContext;}) => voidonTransition
Section titled “onTransition”Called on any navigation (next, skip, or back).
onTransition?: (event: { from: string; to: string; direction: "forward" | "backward"; oldContext: TContext; newContext: TContext;}) => voidonContextUpdate
Section titled “onContextUpdate”Called when context is updated.
onContextUpdate?: (event: { oldContext: TContext; newContext: TContext;}) => voidonSave
Section titled “onSave”Called after state is saved.
onSave?: (state: PersistedFlowState<TContext>) => voidonRestore
Section titled “onRestore”Called after state is restored.
onRestore?: (state: PersistedFlowState<TContext>) => voidonPersistenceError
Section titled “onPersistenceError”Called when save/restore fails.
onPersistenceError?: (error: Error) => voidExamples
Section titled “Examples”Basic usage
Section titled “Basic usage”<Flow flow={myFlow} initialContext={{ name: "" }}> {({ renderStep }) => renderStep({ welcome: <WelcomeStep />, profile: <ProfileStep />, complete: <CompleteStep /> }) }</Flow>With progress indicator
Section titled “With progress indicator”<Flow flow={myFlow}> {({ renderStep, stepId, steps }) => { const stepKeys = Object.keys(steps); const progress = ((stepKeys.indexOf(stepId) + 1) / stepKeys.length) * 100;
return ( <div> <div className="progress-bar" style={{ width: `${progress}%` }} /> {renderStep({ welcome: <WelcomeStep />, profile: <ProfileStep />, complete: <CompleteStep /> })} </div> ); }}</Flow>With persistence
Section titled “With persistence”import { createLocalStorageStore, createPersister } from "@useflow/react";
const store = createLocalStorageStore(localStorage);const persister = createPersister({ store });
<Flow flow={myFlow} persister={persister} saveMode="navigation" loadingComponent={<Spinner />}> {({ renderStep, isRestoring }) => { if (isRestoring) return <Spinner />; return renderStep({ welcome: <WelcomeStep />, profile: <ProfileStep /> }); }}</Flow>With callbacks
Section titled “With callbacks”<Flow flow={myFlow} onNext={(event) => { console.log(`Moving from ${event.from} to ${event.to}`); analytics.track("step_forward", event); }} onComplete={(event) => { console.log("Flow completed!", event.context); submitData(event.context); }}> {({ renderStep }) => renderStep({ welcome: <WelcomeStep />, profile: <ProfileStep /> }) }</Flow>With custom layout
Section titled “With custom layout”<Flow flow={myFlow}> {({ renderStep, stepId, context, back, next, canGoBack, canGoNext }) => ( <div className="custom-layout"> <header> <h1>Step: {stepId}</h1> </header>
<main> {renderStep({ welcome: <WelcomeStep />, profile: <ProfileStep />, complete: <CompleteStep /> })} </main>
<footer> <button onClick={back} disabled={!canGoBack}> Back </button> <button onClick={() => next()} disabled={!canGoNext}> Next </button> </footer> </div> )}</Flow>Multiple instances
Section titled “Multiple instances”function TaskCreator({ taskId }: { taskId: string }) { return ( <Flow flow={taskFlow} instanceId={taskId} // Separate state per task persister={persister} > {({ renderStep }) => renderStep({ details: <DetailsStep />, assign: <AssignStep />, complete: <CompleteStep /> }) } </Flow> );}
// Each instance has independent state<TaskCreator taskId="task-1" /><TaskCreator taskId="task-2" />renderStep helper
Section titled “renderStep helper”The renderStep function takes a map of step components and renders the current step:
renderStep: (elements: Record<string, ReactElement>) => ReactElementUsage:
{({ renderStep }) => renderStep({ step1: <Step1Component />, step2: <Step2Component />, step3: <Step3Component /> })}Type Safety:
TypeScript ensures all steps are provided:
// ❌ Error: Missing step3renderStep({ step1: <Step1 />, step2: <Step2 />})
// ✅ All steps providedrenderStep({ step1: <Step1 />, step2: <Step2 />, step3: <Step3 />})Best practices
Section titled “Best practices”1. Use descriptive instance IDs
Section titled “1. Use descriptive instance IDs”// ✅ Good: Descriptive ID<Flow instanceId={`order-${orderId}`} />
// ❌ Bad: Generic ID<Flow instanceId="1" />2. Handle loading states
Section titled “2. Handle loading states”<Flow flow={myFlow} persister={persister} loadingComponent={<Skeleton />}> {({ renderStep, isRestoring }) => ( isRestoring ? <Spinner /> : renderStep({ /* ... */ }) )}</Flow>3. Memoize callbacks
Section titled “3. Memoize callbacks”const handleComplete = useCallback((event) => { submitData(event.context);}, []);
<Flow onComplete={handleComplete}>4. Use global config for common settings
Section titled “4. Use global config for common settings”// Set defaults in FlowProvider<FlowProvider config={{ persister, saveMode: "navigation" }}> {/* Override only when needed */} <Flow flow={specialFlow} saveMode="always" /></FlowProvider>See also
Section titled “See also”- defineFlow() - Create flows
- useFlowState Hook - Access flow state
- FlowProvider - Global configuration
- Callbacks Guide - Event handling