The upload extension for form-atoms.
npm install @form-atoms/upload-atom
- 🧩 formAtom integrated: Your form submit will wait, while the upload is in progress.
- 🎮 File Input: Ready-to-use file input component.
▶️ Manual or Automatic upload: Start upload on file selection or manually.
import { fromAtom, useForm, useFieldErrors } from "form-atoms";
import { uploadAtom, FileInput, FileUpload } from "@form-atoms/upload-atom";
import { fetchDirectUploadUrl, postFile } from "@/cloudflare";
// 1. define your upload atom using some file service (here Cloudflare Images)
export const cloudflareUploadAtom = uploadAtom(async (file) => {
const { id, uploadUrl } = await fetchDirectUploadUrl();
try {
await postFile(uploadUrl, file);
return id;
} catch {
// Throw string reason for the failure.
throw "Failed to upload.";
}
});
// 2. Use the uploadAtom inside a form as a regular fieldAtom:
const personForm = formAtom({
profilePic: cloudflareUploadAtom(),
});
// Result to render after successful upload:
const Image = ({ url }: { url: FieldAtom<string> }) => {
const value = useFieldValue(url);
return (
<img width={100} height={100} style={{ marginRight: 20 }} src={value} />
);
};
export const Form = () => {
const { fieldAtoms, submit } = useForm(personForm);
const { validateStatus } = useFormStatus(form);
const errors = useFieldErrors(fieldAtoms.profilePic);
return (
<form onSubmit={submit(console.log)}>
<FileUpload atom={fieldAtoms.profilePic}>
{({ isIdle, isLoading, isSuccess, isError }) => (
<div>
{isIdle ? (
<>Please choose a file.</>
) : isLoading ? (
<p>
Please wait... <progress />
</p>
) : isSuccess ? (
<p>
<Image url={fields.profilePic} />
<ins>Done. </ins>
</p>
) : isError ? (
<>
<p>
Failed to upload. Use the <code>useFieldErrors()</code> hook
to display the reason thrown from your <code>upload</code>{" "}
action:
</p>
</>
) : (
<></>
)}
</div>
)}
</FileUpload>
<FileInput atom={fieldAtoms.profilePic} />
{errors.map((error, index) => (
<small key={index}>{error}</small>
))}
<button type="submit" disabled={validateStatus === "validating"}>
{validateStatus === "validating" ? "Submitting..." : "Submit"}
</button>
</form>
);
};See Storybook docs for more.