Getting Started
Idle
Loading
Success
Prerequisites
- Node.js ≥ 16.0.0
- A project using React, Vue, Svelte, Angular, SolidJS, or vanilla JS
Installation
Choose the package for your framework:
bash
npm install @asyncflowstate/react @asyncflowstate/corebash
npm install @asyncflowstate/next @asyncflowstate/react @asyncflowstate/corebash
npm install @asyncflowstate/vue @asyncflowstate/corebash
npm install @asyncflowstate/svelte @asyncflowstate/corebash
npm install @asyncflowstate/angular @asyncflowstate/corebash
npm install @asyncflowstate/solid @asyncflowstate/coreUsing pnpm?
Replace npm install with pnpm add in the commands above.
Your First Flow
React
tsx
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>
);
}That's it. With one hook, you get:
- Automatic loading state
- Double-click prevention (button is disabled while loading)
- ARIA attributes for accessibility
- Error state management
What flow.button() Returns
The button() helper generates the props your button needs:
ts
{
onClick: () => flow.execute(),
disabled: flow.loading,
"aria-busy": flow.loading,
"aria-disabled": flow.loading,
}Adding Error Handling
tsx
function SaveButton() {
const flow = useFlow(async (data) => api.save(data), {
onSuccess: () => toast.success("Saved!"),
onError: (err) => toast.error(err.message),
});
return (
<div>
<button {...flow.button()}>{flow.loading ? "Saving..." : "Save"}</button>
{flow.error && (
<p ref={flow.errorRef} role="alert">
{flow.error.message}
</p>
)}
</div>
);
}Form Handling
tsx
import { 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>
</form>
);
}Global Configuration
Wrap your app with FlowProvider to set defaults for all flows:
tsx
import { FlowProvider } from "@asyncflowstate/react";
function App() {
return (
<FlowProvider
config={{
onError: (err) => toast.error(err.message),
retry: { maxAttempts: 3, backoff: "exponential" },
loading: { minDuration: 400 },
}}
>
<YourApp />
</FlowProvider>
);
}