# AsyncForm

A form that follows the save promise and preserves input when a save fails.

- Family: Forms, imported from `mizu-ui` or `mizu-ui/forms`
- Docs: https://mizu.user64bit.world/components/async-form
- Source: https://github.com/0xuser64bit/mizu/blob/main/packages/mizu/src/forms/AsyncForm.tsx

## Usage

```tsx
"use client";

import { AsyncForm, TextField } from "mizu-ui";

export function Example() {
  return (
    <AsyncForm
      onSubmit={async (data, signal) => {
        const response = await fetch("/api/settings", {
          method: "POST",
          body: data,
          signal,
        });
        if (!response.ok) throw new Error("Could not save settings.");
      }}
    >
      <TextField label="Display name" name="name" required />
      <button type="submit" className="mizu-text-button">
        Save settings
      </button>
    </AsyncForm>
  );
}
```

## Copy it with the shadcn CLI

```sh
npx shadcn@latest init 0xuser64bit/mizu/preset # once, where there is no components.json
npx shadcn@latest add 0xuser64bit/mizu/async-form 0xuser64bit/mizu/text-field
```

## Props

- `onSubmit`: `(data: FormData, signal: AbortSignal) => Promise<void>`. Native validity runs before submission. Signal aborts on unmount.
- `successMessage`: `string` (default `"Saved."`). Successful save feedback.

## Accessibility

Uses native form validation and disables the fieldset while pending. Errors are live; fields stay intact for correction.

## Motion

Pending state follows a real promise, never a simulated timeout.
