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

Routing

useFlow is framework-agnostic. When your UI is split across routes (one route per step), you’ll typically keep the flow state in useFlow and use your router to show the right screen for the current step.

In this architecture:

  • Your router owns what screen is visible
  • useFlow owns what step is current, the path/history, and the context

The simplest integration is one-way sync: when the flow transitions, you navigate.

Expo Router (React Native): one route per step

Section titled “Expo Router (React Native): one route per step”

Mount <Flow /> in a stable layout (so it doesn’t unmount between step routes), and use onTransition to navigate.

app/(onboarding)/_layout.tsx
import { Flow } from "@useflow/react";
import { router, Stack } from "expo-router";
import { onboardingFlow } from "@/features/onboarding/flow";
type OnboardingStepId = keyof typeof onboardingFlow.config.steps & string;
function getOnboardingRoute(stepId: OnboardingStepId) {
return `/onboarding/${stepId}` as const;
}
export default function OnboardingLayout() {
return (
<Flow
flow={onboardingFlow}
onTransition={({ to, direction }) => {
const route = getOnboardingRoute(to);
if (direction === "backward") {
router.back();
return;
}
router.push(route);
}}
>
{() => (
<Stack
screenOptions={{
headerShown: false,
gestureEnabled: false,
animation: "slide_from_right",
}}
/>
)}
</Flow>
);
}

Most apps only need Flow → Router sync. Two-way sync (Router → Flow) is only needed for advanced cases:

  • Deep linking directly to a mid-flow route
  • The user navigates via native back/gesture and you want flow stepId/path to match
  • Restoring a persisted flow state and ensuring the router reflects the restored step