FlowOutlet
FlowOutlet is where the flow appears in the UI.
It renders the active step, the loading state, the error state, and the idle
content for the flow host.
FlowOutlet is not exposed directly. It is returned as SequentOutlet from
useSequentFlow, which binds the outlet to its
control functions and reactive state.
Props
type SequentOutletProps = {
children?: ReactNode;
fallback?: ReactNode;
errorStep?: (context: ErrorStepContext) => ReactNode;
chrome?: (children: ReactNode) => ReactNode;
transition?: (props: TransitionSlotProps) => ReactNode;
};
type ErrorStepContext = {
error: unknown;
componentStack: string | null;
failedStep: ComponentType;
};
type TransitionSlotProps = {
previousStep: ReactNode | null;
nextStep: ReactNode;
phase: "exiting" | "entering" | "exited";
onExited: () => void;
transitionKey: number;
};
children
Content rendered when no flow is active.
See: Idle state below.
fallback
The placeholder shown while the active step is loading.
This is the loading surface for async step loaders before the step becomes available.
Behavior:
- shown only while the current step is loading, defined as the time between an async step loader being activated and its promise resolving
- hidden once the step is ready
- if omitted, no loading UI is shown
- should be lightweight, because it may appear during transitions
errorStep
Custom error UI for a step that fails while rendering or during a step transition.
The callback receives an ErrorStepContext object with:
error— the thrown valuecomponentStack— the React component stack when availablefailedStep— the step component that failed
Behavior:
- used when the active step throws an error
- replaces the active step UI with your error UI
- if omitted, the outlet renders no special error UI
chrome
A wrapper for the active step area.
This is the surrounding UI for the active step, such as:
- a layer and frame for a modal or side panel
- a card or sheet layout
- a header with a close button
- a progress indicator
- a fixed footer or toolbar
Behavior:
- receives the rendered step area as
children - wraps the active step and any loading or error UI inside that shell
- is not used while the outlet is idle
- if omitted, the outlet renders the step area directly
transition
An optional render prop that enables animated step transitions.
When provided, the outlet enters transition mode: instead of immediately swapping steps, it renders both the exiting and entering step simultaneously and passes them to this render prop along with phase information and lifecycle callbacks.
The consumer wires their animation library (CSS, framer-motion, GSAP, etc.) to
the provided signals. Ready-made CSS transitions are available from
react-sequent/transitions — see Transitions.
Step subtrees are rendered through portals into stable hosts, so previousStep
and nextStep are lightweight placeholders you can position anywhere in your
layout — the outgoing step keeps its component instance (local state and
effects intact) while it animates out, even though your wrapper structure
changes between phases.
TransitionSlotProps:
previousStep— the exiting step element.nullwhen no transition is in flight.nextStep— the entering/current step element. Always present when a flow is active.phase— current transition phase:"exiting"— previous step is animating out; callonExitedwhen done."entering"— previous step has unmounted; enter-animation window for one tick."exited"— the flow is settled; no animation in progress.
onExited— call to signal the exit animation has completed. Only meaningful during the"exiting"phase.transitionKey— monotonically increasing identity for the current transition. Attach as a Reactkeyon animation wrapper elements to force remounting across back-to-back transitions.
Constraints:
- A step may only initiate one transition — subsequent
advance()/retreat()calls from the same step render are silently dropped. - While in
"exiting"phase, the entering step may enqueue a navigation. resolve()/abort()during any phase tears down both steps immediately;onExitedis not expected.- Chrome wraps the transition output and is never unmounted during a transition.
- When omitted, step swaps are immediate (current behavior).
Public states
The outlet has a small set of user-facing states that matter when you build against it.
Idle
No flow is currently active.
What you see:
children
What this means:
- this is the default state before a flow starts
- idle content reappears after a flow ends
- this is the place for entry UI or post-flow content
Active
A flow is in progress and the current step is being shown.
What you see:
- the active step
- optional
chromearound that step
What this means:
- the step uses the flow and step hooks to continue, resolve, or abort the flow
- the outlet no longer renders the idle
children
Transitioning
When the transition prop is provided, step changes pass through animated phases
instead of swapping instantly.
What you see depends on phase:
exiting— both the previous and next step are mounted. The consumer plays an exit animation.entering— the previous step has unmounted. The next step renders alone (enter-animation window, one tick).exited— the flow is settled; same as the normal Active state.
The transition render prop receives all step elements, the current phase,
onExited (call to complete the exit), and transitionKey (for animation
wrapper remounting).
Loading
The current step is not yet ready because its loader is asynchronous.
What you see:
fallback
What this means:
- this state is temporary
- it exists only for async step loaders
- the current flow remains in progress while the fallback is shown
Error
The active step failed.
What you see:
- the result of
errorStep, if provided
What this means:
- the error UI may describe the failure and can provide a retry path
- the outlet remains in an error state until the flow changes or is restarted
Finished
The flow resolved or was aborted.
What you see:
- the outlet returns to idle and renders
childrenagain
What this means:
- the flow is no longer active
- this is the terminal outcome for the current run of the flow
- the next flow starts from the idle state again
Example
function CheckoutShell() {
const { SequentOutlet } = useSequentFlow();
return (
<SequentOutlet
fallback={<div>Loading step…</div>}
errorStep={({ error }) => (
<div>
<h2>Something went wrong</h2>
<pre>{String(error)}</pre>
</div>
)}
chrome={(step) => <div className="sheet">{step}</div>}
>
<main>
<p>Nothing is active yet.</p>
</main>
</SequentOutlet>
);
}
Relationship to useSequentFlow
useSequentFlow returns the flow controller and the bound outlet component together.
The outlet defines the visible shell for the flow. The control functions start the flow and report its status and result.
A good mental model is:
- the hook starts and reports on the flow
- the outlet renders the flow
- the steps perform the flow logic
Conclusion
The outlet is the place where flow UI lives:
childrenfor idle contentfallbackfor loading UIerrorStepfor error UIchromefor surrounding layout
That keeps the outlet easy to reason about and keeps flow logic inside the hooks and steps where it belongs.