form-mate

A tiny and elegant library to handle forms with React:

Usage no npm install needed!

<script type="module">
  import formMate from 'https://cdn.skypack.dev/form-mate';
</script>

README

Form Mate npm install form-mate test badge gzip size

A tiny and elegant library to handle forms with React:

import Form from "form-mate";

// { fullname: "Francisco", email: "xxxxxx@francisco.io" }
export default () => (
  <Form onSubmit={data => console.log(data)}>
    <input name="fullname" required />
    <input name="email" type="email" required />
    <button>Subscribe!</button>
  </Form>
);

Benefits over a plain <form>:

  • Parse the form fields into an object for easy access and consumption. Now you can do onSubmit(item => setItems([...items, item])).
  • Disable the form while it's submitting to avoid double-submit.
  • Prop onError for easy error handling, integrates well with other options.
  • Prop onChange to listen to the form changes as they happen.
  • Prop autoReset to clear the form after onSubmit finishes successfully.
  • Prop encType (like encType="multipart/form-data") for file handling. It makes the callback receive an instance of FormData instead of a plain object. This makes it easy to submit files with fetch(), Axios, etc.

Getting Started

Install form-mate with npm:

npm install form-mate

Import it and use it anywhere in your React project:

import Form from "form-mate";

export default () => (
  <Form onSubmit={data => console.log(data)} autoReset>
    {/* ... */}
  </Form>
);

API

onSubmit

Mandatory prop that accepts a sync or async callback. It will receive the values in the form when submitted:

import Form from "form-mate";

export default function Subscribe() {
  return (
    <Form onSubmit={data => console.log(data)}>
      <input name="name" defaultValue="Francisco" />
      <input name="subscribe" defaultChecked type="checkbox" />
      <input name="terms" value="accepted" defaultChecked type="checkbox" />
      <input name="gender" value="female" type="radio" />
      <input name="gender" value="male" defaultChecked type="radio" />
      <button>Subscribe!</button>
    </Form>
  );
}
// {
//   name: "Francisco",
//   subscribe: "on", // The default when no "value" is provided
//   terms: "accepted",
//   gender: "male"
// }

It prevents the default action automatically. See the tests for more examples of how the fields are parsed.

onError

Optional prop to handle any error happening in the onSubmit. This allows the onSubmit to fail as desired. Works well with both sync and async onSubmit:

import Form from "form-mate";

export default () => {
  const [error, setError] = useState();
  const onSubmit = data => {
    throw new Error("Aaaaagh");
  };
  return (
    <Form onSubmit={handleForm} onError={setError} autoReset>
      {error ? <p>{error.message}</p> : null}
      <input name="fullname" required />
      <input name="email" type="email" required />
      <button>Subscribe!</button>
    </Form>
  );
};

onChange

Listen to the forms' changes in fields as they happen. It will be triggered on every keystroke, on every click (that changes the data), etc:

import Form from "form-mate";

export default function Subscribe() {
  return (
    <Form onChange={data => console.log(data)}>
      <input name="name" defaultValue="Francisco" />
      <input name="subscribe" defaultChecked type="checkbox" />
      <input name="terms" value="accepted" defaultChecked type="checkbox" />
      <input name="gender" value="female" type="radio" />
      <input name="gender" value="male" defaultChecked type="radio" />
      <button>Subscribe!</button>
    </Form>
  );
}

autoReset

By default the form is not reset after it's submitted. This prop can make the form to reset after the onSubmit callback has resolved successfully:

<Form onSubmit={...} autoReset>...</Form>

Even with this prop, the form will not be reset if the onSubmit throws an error (sync or async).

This prop is very useful when adding new items to a list in succession, see codesandbox example.

encType

The encType can be set to multipart/form-data to upload files:

import Form from 'form-mate';

export default() => (
  <Form onSubmit={...} encType="multipart/form-data">
    <input name="name" />
    <input type="file" name="file" />
    <button>Send</button>
  </Form>
);

In that case, the argument data passed to the onSubmit will not be a plain object, it will be an instance of FormData instead.

Examples

Add items to a list

A fully working shopping list example (see codesandbox):

import React, { useState } from "react";
import Form from "./Form";

export default function Groceries() {
  const [items, setItems] = useState([]);
  return (
    <ul>
      {items.map(item => (
        <li key={item.text}>
          {item.text} × {item.quantity}
        </li>
      ))}
      <li>
        <Form onSubmit={item => setItems([...items, item])} autoReset>
          <input name="text" placeholder="Item to buy" autoFocus />
          {" × "}
          <input type="number" name="quantity" defaultValue={1} />
          <button>Add!</button>
        </Form>
      </li>
    </ul>
  );
}

Upload files with React

To upload files with React and Axios, you can do it like this:

import Form from 'form-mate';

export default function App() {
  const onSubmit = data => {
    // Send the data to the server
    await axios.post("/hello", data, { "Content-Type": "multipart/form-data" });
  };
  return (
    <Form onSubmit={onSubmit} encType="multipart/form-data">
      <input name="name" />
      <input type="file" name="file" />
      <button>Send</button>
    </Form>
  );
}