Skip to content

form-atoms/upload-atom

Repository files navigation

@form-atoms/upload-atom

The upload extension for form-atoms.

npm install @form-atoms/upload-atom
Bundlephobia NPM Version Code coverage

Features

  • 🧩 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.

Quick Start

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.

About

The upload extension for Jotai form-atoms

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •