Skip to content
⚠️ Beta: API may change before v1.0. Pin to ~0.x.0 to avoid breaking changes.

FlowProvider

Configure global defaults for all flows in your application. Provides shared persistence, callbacks, and configuration.

import { FlowProvider } from "@useflow/react";
import { FlowProvider, createLocalStorageStore, createPersister } from "@useflow/react";
const globalPersister = createPersister({
store: createLocalStorageStore()
});
function App() {
return (
<FlowProvider
persister={globalPersister}
saveMode="navigation"
saveDebounce={500}
>
<YourApp />
</FlowProvider>
);
}
PropTypeDefaultDescription
persisterFlowPersister-Default persister for all flows
saveMode"always" | "navigation" | "manual""navigation"When to auto-save
saveDebouncenumber300Debounce delay in ms
onPersistenceError(error: Error) => void-Global error handler
CallbackTypeDescription
onFlowStart(event) => voidWhen any flow starts
onFlowComplete(event) => voidWhen any flow completes
onStepTransition(event) => voidWhen any step changes

All flows will use this persister by default:

const persister = createPersister({
store: createLocalStorageStore(),
ttl: 7 * 24 * 60 * 60 * 1000 // 7 days
});
<FlowProvider persister={persister}>
{/* All flows inherit this persister */}
<Flow flow={onboardingFlow}>
{/* Uses global persister */}
</Flow>
<Flow flow={checkoutFlow} persister={customPersister}>
{/* Override with custom persister */}
</Flow>
</FlowProvider>

Track all flow events in one place:

<FlowProvider
onFlowStart={({ flowId, variantId, context }) => {
analytics.track('flow_started', {
flow_id: flowId,
variant: variantId,
initial_context: context
});
}}
onStepTransition={({ flowId, from, to, direction }) => {
analytics.track('step_changed', {
flow_id: flowId,
from_step: from,
to_step: to,
direction
});
}}
onFlowComplete={({ flowId, context }) => {
analytics.track('flow_completed', {
flow_id: flowId,
final_context: context
});
}}
>
<YourApp />
</FlowProvider>

Centralized error handling for all flows:

<FlowProvider
onPersistenceError={(error) => {
// Log to error service
errorReporter.log(error);
// Show user notification
if (error.name === 'QuotaExceededError') {
toast.error('Storage full - some progress may not be saved');
}
}}
>
<YourApp />
</FlowProvider>

Configure save behavior globally:

<FlowProvider
saveMode="always" // Save on every change
saveDebounce={1000} // Wait 1 second before saving
>
{/* All flows use these settings */}
<Flow flow={surveyFlow}>
{/* Saves automatically on every change */}
</Flow>
<Flow flow={checkoutFlow} saveMode="manual">
{/* Override to manual saving */}
</Flow>
</FlowProvider>

Use the useFlowConfig hook to access provider configuration:

import { useFlowConfig } from "@useflow/react";
function MyComponent() {
const config = useFlowConfig();
return (
<div>
Save mode: {config?.saveMode || 'navigation'}
Has persister: {config?.persister ? 'Yes' : 'No'}
</div>
);
}
type FlowStartEvent = {
flowId: string;
variantId?: string;
instanceId?: string;
context: FlowContext;
}
type StepTransitionEvent = {
flowId: string;
variantId?: string;
instanceId?: string;
from: string;
to: string;
direction: "forward" | "backward";
oldContext: FlowContext;
newContext: FlowContext;
}
type FlowCompleteEvent = {
flowId: string;
variantId?: string;
instanceId?: string;
context: FlowContext;
}
const isDevelopment = process.env.NODE_ENV === 'development';
<FlowProvider
persister={isDevelopment ? undefined : persister}
saveMode={isDevelopment ? "manual" : "navigation"}
onStepTransition={isDevelopment ? console.log : undefined}
>
<App />
</FlowProvider>

Different configs for different parts of your app:

function App() {
return (
<>
{/* Onboarding flows with long TTL */}
<FlowProvider persister={onboardingPersister}>
<OnboardingSection />
</FlowProvider>
{/* Checkout flows with short TTL */}
<FlowProvider persister={checkoutPersister}>
<CheckoutSection />
</FlowProvider>
</>
);
}

Update provider config based on user preferences:

function App() {
const [saveEnabled, setSaveEnabled] = useState(true);
return (
<FlowProvider
persister={saveEnabled ? persister : undefined}
saveMode={saveEnabled ? "navigation" : "manual"}
>
<Settings onToggleSave={setSaveEnabled} />
<YourFlows />
</FlowProvider>
);
}
// ❌ Too much in provider
<FlowProvider
persister={persister}
saveMode="always"
saveDebounce={100}
onFlowStart={...}
onStepTransition={...}
onFlowComplete={...}
onPersistenceError={...}
>
// ✅ Only shared config
<FlowProvider
persister={persister}
onPersistenceError={handleError}
>
<FlowProvider persister={defaultPersister}>
{/* Uses default */}
<Flow flow={flow1} />
{/* Override for specific flow */}
<Flow
flow={flow2}
persister={customPersister}
saveMode="always"
/>
</FlowProvider>
import { FlowProvider, createMemoryStore } from "@useflow/react";
const testPersister = createPersister({
store: createMemoryStore()
});
function renderWithProvider(ui: ReactElement) {
return render(
<FlowProvider persister={testPersister}>
{ui}
</FlowProvider>
);
}
test('flow completes', () => {
renderWithProvider(<MyFlow />);
// Test with persistence enabled
});

The provider is fully typed:

import { FlowProvider, FlowProviderProps } from "@useflow/react";
const config: FlowProviderProps = {
persister: myPersister,
saveMode: "navigation",
callbacks: {
onFlowStart: ({ flowId }) => console.log(flowId)
}
};
<FlowProvider {...config}>
<App />
</FlowProvider>