| name | flowness |
| description | React state-machine flow library for building multi-step UIs (wizards, onboarding, signup flows, checkout). Use when: (1) Creating multi-step flows with React using flowness, (2) Adding steps, transitions, guards, or actions to a flowness Flow, (3) Working with flowness createFlow, Flow, Flow.Step, or useFlow, (4) Implementing wizard-like navigation with typed context and events, (5) User mentions 'flowness' or imports from 'flowness'. Trigger phrases: 'flowness', 'createFlow', 'multi-step flow', 'wizard flow', 'step machine', 'flow transitions'. |
Flowness
Declarative React state-machine for multi-step UI flows. One factory call (createFlow) produces typed Flow, Step, and useFlow — no external state library needed.
Quick Start
import { createFlow } from 'flowness';
interface Ctx {
email: string;
[key: string]: unknown;
}
type Ev = { type: 'NEXT'; payload: { email: string } } | { type: 'BACK' };
const { Flow, useFlow } = createFlow<Ctx, Ev>();
function EmailStep() {
const { send, state } = useFlow();
return (
<div>
<p>Current: {state.context.email}</p>
<button onClick={() => send({ type: 'NEXT', payload: { email: 'a@b.com' } })}>
Next
</button>
</div>
);
}
function App() {
return (
<Flow initial="email" initialContext={{ email: '' }}>
<Flow.Step
stepId="email"
transitions={[
{
to: 'done',
guard: (_ctx, e) => e.type === 'NEXT',
action: (ctx, e) => ({
...ctx,
email: (e as Extract<Ev, { type: 'NEXT' }>).payload.email,
}),
},
]}
>
<EmailStep />
</Flow.Step>
<Flow.Step stepId="done">
<p>Done!</p>
</Flow.Step>
</Flow>
);
}
Core Concepts
createFlow<C, E>()
Call at module level. Returns { Flow, Step, useFlow, context }.
C — context shape (must extend { [key: string]: unknown })
E — event union (must have type: string)
Flow Props
| Prop | Type | Required | Default | Description |
|---|
initial | string | Yes | — | First step ID |
initialContext | C | Yes | — | Starting context |
onChange | (state) => void | No | — | Fires after every transition |
allowFreeTransition | boolean | No | false | Allow go()/tryGo() without defined transitions |
Flow.Step Props
| Prop | Type | Required | Description |
|---|
stepId | string | Yes | Unique step identifier |
children | ReactNode | No | Rendered when step is active |
onEnter | (ctx) => ctx | No | Lifecycle: entering step |
onExit | (ctx) => ctx | No | Lifecycle: leaving step |
transitions | Transition[] | No | Allowed transitions from this step |
useFlow() → FlowRuntime
| Method | Description |
|---|
state | { current, context, history } |
send(event) | Dispatch event → evaluate guards → transition |
go(stepId) | Direct transition (throws on failure) |
tryGo(stepId, patch?) | Direct transition (returns boolean, optional context patch) |
setContext(updater) | Update context without transitioning |
Transitions
{
to: 'targetStep',
guard: (ctx, event) => bool,
priority: 10,
else: true,
action: (ctx, event) => ctx,
}
Guard evaluation: sequential → sort passing by priority → highest wins → fallback to else.
Both guard and action can be async.
Key Rules
- Context type MUST include
[key: string]: unknown index signature
createFlow() must be called at module level (not inside components)
useFlow() only works inside components rendered as Flow.Step children
- Steps can be wrapped in layout divs — Flow recursively discovers
Step components
- Duplicate
stepId throws an error
- Ships with
"use client" — works in Next.js App Router directly
- Use
Extract<EventUnion, { type: 'X' }> to narrow event types in guards/actions
Full API Reference
For complete type definitions, all method signatures, async patterns, stale transition protection, and advanced usage → read references/api.md.