The industry-standard engine for predictable async UI behavior.
Eliminate boilerplate and bugs in async interactions. Built for React, Vue, Svelte, Angular, Solid — works everywhere.
Every modern application has async user actions: clicking buttons, submitting forms, saving data, deleting items, uploading files, or making payments.
Each of these actions follows the same lifecycle:
idle → loading → success → error → retry
But in real projects, this logic is re-written thousands of times. This leads to:
- Double submission bugs (user clicks twice)
- Inconsistent loading UX (spinners everywhere, or nowhere)
- Error handling chaos (forgotten catch blocks, messy transitions)
- Boilerplate fatigue (writing
setIsLoading(true)on every function)
AsyncFlowState provides a standard, reusable engine to control async UI behavior correctly and consistently. It is not a data-fetching library (like React Query) or a state manager (like Redux); it is a behavior orchestrator.
- Double submissions? Prevented by default.
- Optimistic UI? One line of config, with Deep-Diff Rollbacks.
- Global Undo? Purgatory delays destructive actions dynamically.
- Worker Offloading? Push intense ops to a background thread instantly (
flow.worker()). - Ghost Queues? Spam clicking handled via background queues seamlessly.
- Dead Letter Queue (DLQ)? Failed operations automatically pooled for replay.
- Feature Convergence?
cross-tab sync,AI Skeletons, andPredictive Prefetchingbuilt right in.
vs. Manual State Management
- 90% less boilerplate
- Zero double-submission bugs
- Enterprise-grade error resilience (DLQ, Retries, Circuit Breakers)
vs. React Query / SWR
- Designed for actions, not data fetching
- Works with any async function (API, WebSockets, LocalStorage, LLMs)
- Powerful composition (
pipe,chain,raceFlows)
vs. Redux / Zustand
- Focused on behavior orchestration, not pure global state
- Time-Travel debugger with
exportState()directly built-in - Works flawlessly alongside your existing state manager
Here is a common scenario: Submitting a form and handling the API response.
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (data) => {
setLoading(true);
setError(null);
try {
await api.save(data);
alert("Saved!");
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
return (
<button onClick={handleSubmit} disabled={loading}>
{loading ? "Saving..." : "Save"}
</button>
);const flow = useFlow(api.save, {
onSuccess: () => alert("Saved!"),
});
return (
<button {...flow.button()}>{flow.loading ? "Saving..." : "Save"}</button>
);See more patterns? Check out our Full Comparison Guide.
AsyncFlowState is built as a modular system:
| Package | Version | Description | NPM |
|---|---|---|---|
@asyncflowstate/core |
2.0.2 |
Framework-agnostic logic engine | |
@asyncflowstate/react |
2.0.2 |
React hooks & accessibility-first helpers | |
@asyncflowstate/next |
2.0.2 |
Next.js Server Actions & SSR integration | |
@asyncflowstate/vue |
2.0.2 |
Vue 3 composables & provide/inject config | |
@asyncflowstate/svelte |
2.0.2 |
Svelte stores with $ auto-subscription |
|
@asyncflowstate/angular |
2.0.2 |
Angular Observable/BehaviorSubject bindings | |
@asyncflowstate/solid |
2.0.2 |
SolidJS fine-grained reactive signals |
AsyncFlowState consists of a core engine and native adapters for each framework.
| Framework | Command |
|---|---|
| React | npm install @asyncflowstate/react @asyncflowstate/core |
| Next.js | npm install @asyncflowstate/next @asyncflowstate/react @asyncflowstate/core |
| Vue 3 | npm install @asyncflowstate/vue @asyncflowstate/core |
| Svelte | npm install @asyncflowstate/svelte @asyncflowstate/core |
| Angular | npm install @asyncflowstate/angular @asyncflowstate/core |
| SolidJS | npm install @asyncflowstate/solid @asyncflowstate/core |
import { useFlow } from "@asyncflowstate/react";
function SaveButton() {
const flow = useFlow(async (data) => {
return await api.save(data);
});
return (
<button {...flow.button()}>
{flow.loading ? "Saving..." : "Save Changes"}
</button>
);
}"use client";
import { useServerActionFlow } from "@asyncflowstate/next";
import { saveUserAction } from "./actions";
function ProfileForm() {
const flow = useServerActionFlow(saveUserAction);
return (
<form action={flow.execute}>
<input name="name" />
<button type="submit" disabled={flow.loading}>
{flow.loading ? "Saving..." : "Save"}
</button>
</form>
);
}import { useFlowSequence } from "@asyncflowstate/react";
const steps = [
{ name: "Step 1", flow: flow1 },
{ name: "Step 2", flow: flow2, mapInput: (prev) => prev.id },
];
const sequence = useFlowSequence(steps);
// sequence.execute() runs them in orderimport { z } from "zod";
const schema = z.object({
username: z.string().min(3),
email: z.string().email(),
});
function ProfileForm() {
const flow = useFlow(updateProfile);
return (
<form {...flow.form({ schema, extractFormData: true })}>
<input name="username" />
{flow.fieldErrors.username && <span>{flow.fieldErrors.username}</span>}
<input name="email" />
{flow.fieldErrors.email && <span>{flow.fieldErrors.email}</span>}
<button type="submit" disabled={flow.loading}>
Submit
</button>
{flow.error && <p ref={flow.errorRef}>{flow.error.message}</p>}
</form>
);
}import { FlowProvider } from "@asyncflowstate/react";
function App() {
return (
<FlowProvider
config={{
onError: (err) => toast.error(err.message),
retry: { maxAttempts: 3, backoff: "exponential" },
}}
>
<YourApp />
</FlowProvider>
);
}pnpm add @asyncflowstate/vue @asyncflowstate/core<script setup lang="ts">
import { useFlow } from "@asyncflowstate/vue";
const { loading, data, execute, button } = useFlow(async (id: string) =>
api.fetchUser(id),
);
</script>
<template>
<button v-bind="button()">
{{ loading ? "Loading..." : "Fetch User" }}
</button>
</template>pnpm add @asyncflowstate/svelte @asyncflowstate/core<script>
import { createFlow } from '@asyncflowstate/svelte';
const flow = createFlow(async (id) => api.fetchUser(id));
</script>
<button on:click={() => flow.execute('user-123')} disabled={$flow.loading}>
{$flow.loading ? 'Loading...' : 'Fetch User'}
</button>pnpm add @asyncflowstate/angular @asyncflowstate/coreimport { createFlow } from "@asyncflowstate/angular";
@Component({
template: `
<ng-container *ngIf="userFlow.state$ | async as state">
<button (click)="userFlow.execute('user-123')" [disabled]="state.loading">
{{ state.loading ? "Loading..." : "Fetch User" }}
</button>
</ng-container>
`,
})
export class UserComponent implements OnDestroy {
userFlow = createFlow(async (id: string) => api.fetchUser(id));
ngOnDestroy() {
this.userFlow.destroy();
}
}pnpm add @asyncflowstate/solid @asyncflowstate/coreimport { createFlow } from "@asyncflowstate/solid";
function UserCard() {
const flow = createFlow(async (id: string) => api.fetchUser(id));
return (
<button onClick={() => flow.execute("user-123")} disabled={flow.loading()}>
{flow.loading() ? "Loading..." : "Fetch User"}
</button>
);
}- Global Config: Set default options app-wide with
FlowProvider. - Declarative Chaining: Orchestrate complex workflows with
triggerOnandsignalsinstead of manualuseEffect. - Streaming Support: Native support for LLM/AI streaming using
AsyncIterableorReadableStream. - Parallel & Sequential: Orchestrate multiple flows with aggregate state via
FlowParallelandFlowSequence. - Declarative Polling: Built-in support for auto-refreshing actions with conditional stop logic.
- Smart Persistence: Survive page refreshes and resume interrupted operations (file uploads, forms, etc.).
- Persistent Circuit Breaker: Prevent cascading failures with cross-session state persistence.
- Visual Sequence Trace: Real-time Timeline/Gantt view of all async activity with
FlowDebugger. - Form Recovery: Automatically re-focus fields and restore validation errors after a page refresh.
- Global Notifications: Centralized success/error handling for all flows via
FlowNotificationProvider. - Core Engine: Lightweight runtime logic that works anywhere (Vanilla JS, Node, etc.).
- Examples - Check out full patterns for Login, File Uploads, Advanced Forms, and more.
- Core Package Documentation - The framework-agnostic engine API.
- React Package Documentation - Hooks, helpers, and accessibility components.
- Vue Package Documentation - Vue 3 composables.
- Svelte Package Documentation - Svelte stores.
- Angular Package Documentation - Observable/BehaviorSubject bindings.
- Solid Package Documentation - SolidJS reactive primitives.
We love contributions! Whether you're fixing a bug, improving documentation, or suggesting a new feature, your help is welcome.
- Check for Issues: Look at our Issue Tracker to see if your idea or bug is already being addressed.
- Follow the Guide: Read our Contributing Guide for local setup, development workflows, and coding standards.
- Submit a PR: When you're ready, use our Pull Request Template to ensure your contribution meets our quality standards.
We take the security of AsyncFlowState seriously. If you find a potential vulnerability, please do not open a public issue. Instead, follow the instructions in our Security Policy to report it responsibly.
To ensure a welcoming, diverse, and inclusive community, we adhere to the Contributor Covenant Code of Conduct. By participating in this project, you agree to abide by its terms.
MIT © AsyncFlowState Contributors
"Stop rewriting the same async logic. Start building features."
AsyncFlowState solves async UI behavior once, correctly, and everywhere.