Screens are a typed map of screen IDs to React components or string IDs. Keys become the valid screen IDs for the whole flow, so invalid targets like "settings" are caught at compile time.
Pass React.lazy components the same way — wrap <ScreenComponent /> in Suspense in your app.
The library does not own app state. Pass state into the hook:
useScreenFlow(flowDefinition, state);
That works with React state, Zustand, Redux, server data, React Router loaders, or any other source.
resolveStartresolveStart(state) tells the flow where the user belongs based on current state. Useful for auth and profile flows where users refresh or deep link.
With autoNavigate: true, the hook re-runs this when external state changes.
Each screen can define:
next — next screen or a function of stateprev — previous screen or a function of statecanGoNext — guard for forward navigationcanGoPrev — guard for back navigationcanGoTo — guard for direct navigationnext and prev can be functions, so branching stays in the flow definition instead of button handlers. All returned screen IDs are checked against the screens map.
Guards read state your app already provides and return a boolean or a decision object:
canGoNext: (state) => Boolean(state.user);
canGoNext: () => ({
allowed: false,
message: "Session expired.",
redirectTo: "login",
code: "SESSION_EXPIRED",
});
Fetch requests, login calls, payment validation, and state updates belong in your app actions, loaders, effects, or store logic — not inside flow declarations.
react-screen-flow is smaller than a state machine and more focused than a router. It fits when you need typed screen flows with guards, async checks, and loading behavior, without the library owning app state or URL routing.
| Tool | Best At | Where react-screen-flow Fits |
|---|---|---|
| React Router / TanStack Router | URLs, nested layouts, route loaders. | Router for URLs; this library for which screen is allowed. |
| XState / Zag / Robot | Full state machines, events, parallel states. | Screens plus guards, not a full statechart. |
| react-use-wizard / use-wizard | Linear next/back wizard state. | Branching, async guards, redirects, and refresh recovery. |
| Form libraries | Multi-step form validation. | Form state stays in the form; pass readiness into the flow. |
| Redux / Zustand / Jotai | App-wide state. | Store holds facts; flow reads state and returns decisions. |