Skip to main content

Getting Started

Welcome! 👋 Usetheform is a lightweight, dependency-free React library for composing declarative forms and managing their state. It's simple to use, flexible, and powerful for handling nested fields, validation, and much more.

ðŸ”Ĩ Features​

ðŸ“Ķ Easy integration with libraries like React Select/Material UI and React Dropzone/Material UI Dropzone
✅ Sync and Async validation
🔍 Schema support for Yup, Zod, Joi, Superstruct
🧎 Follows native HTML standards - see in action
ðŸ§Đ Easy handling of arrays, objects, and nested structures - Nested collections, arrays, and objects made simple
ðŸ’Ĩ Tiny bundle size, zero dependencies — Check it on Bundlephobia

🧠 Motivation​

Usetheform was created to provide a highly flexible, declarative way to handle forms in React with no dependencies. It supports:

ðŸ§Đ Nested field structures: Build deeply structured forms with collections, arrays, and objects.
🔁 Synchronous & asynchronous validation: Validate inputs at every level — instantly or with async logic.
🧠 Custom input and reducer logic: Extend functionality using reducers and custom input components.
📜 Schema-based validation: Integrate with Yup, Zod, Superstruct, or Joi for powerful schema validation.
ðŸŠķ Tiny footprint: Zero dependencies and optimized for performance.

If you find this library useful, please ⭐ the repo. It means a lot! 🙏

🚀 Installation​

Install usetheform using your preferred package manager:

npm install usetheform --save

⚡ Quickstart​

Preview
Live Editor
<Form onSubmit={state => alert(JSON.stringify(state))}>
    <Collection object name="user" as="div" className="mt-10 flex space-x-4">
      <Input type="text" name="name" placeholder="Enter your name" />
      <Input type="text" name="surname" placeholder="Enter your surname" />
    </Collection>
    <Collection as="div" object name="nested" className="flex space-x-4 justify-between">
      <Input type="text" name="name" placeholder="Enter nested name" />
      <Input type="text" name="surname" placeholder="Enter nested surname" />
    </Collection>
    <div className="mt-6 flex space-x-4">
      <Input type="radio" name="gender" placeholder="M" value="M" />
      <Input type="radio" name="gender" placeholder="F" value="F" />
      <Input type="radio" name="gender" placeholder="Other" value="Other" />
    </div>
    <Collection object name="options" as="div" className="flex space-x-4">
        <Input type="checkbox" name="one" placeholder="Opt 1" checked />
        <Input type="checkbox" name="second" placeholder="Opt 2" />
    </Collection>
  <Collection as="div" array name="media" className="mt-6 flex justify-center space-x-4">
      <Input type="file" />
      <Input type="file" />
      <Input type="file" />
  </Collection>
  <div className="mt-6 flex justify-center space-x-4">
    <Submit />
    <Reset />
  </div>
</Form>

📖 Recipes​

🙈 First: create a form store​

FormTypes.tsx
interface FormState { counter: number; }
AwesomeFormStore.tsx
import { createFormStore } from 'usetheform';
import { FormState } from "./FormTypes";

const [formStore, useFormSelector] = createFormStore<FormState>({ counter: 0 });

export const awesomeFormStore = formStore;
export const useAwesomeFormSelector = useFormSelector;

🙉 Next: create your awesome Form​

AwesomeForm.tsx
import { Form } from 'usetheform';
import { awesomeFormStore } from './awesomeFormStore';
import { FormState } from "./FormTypes";

export default function AwesomeForm() {
return (
<>
<Form<FormState> formStore={awesomeFormStore}>
<Input type="number" name="counter" value="0" placeholder="Counter" />
</Form>
<Counter />
</>
);
}

🙊 Finally: bind your components, and that's it!​

Use the useAwesomeFormSelector hook anywhere, no providers needed. Select your state and the component will re-render on changes.

Counter.tsx
import { useAwesomeFormSelector } from './awesomeFormStore'

export const Counter = () => {
const [counter, setCounterValue] = useAwesomeFormSelector<"counter">((state) => state.counter);
return (
<div>
<span>{counter}</span>
<button type="button" onClick={() => setCounterValue((prev) => ++prev)}>
Increase Counter
</button>
<button type="button" onClick={() => setCounterValue((prev) => --prev)}>
Decrease Counter
</button>
<button type="button" onClick={() => setCounterValue(0)}>
Reset Counter
</button>
</div>
);
}

CodeSandbox Examples​

  • Twitter 'What's Happening' Form Bar: Sandbox
  • Shopping Cart: Sandbox
  • Examples: Slider, Select, Collections etc..: Sandbox
  • Various Implementations: Sandbox
  • Wizard: Sandbox
  • FormContext: Sandbox
  • Material UI - React Select: Sandbox
  • Validation using Yup, ZOD, JOI, Superstruct: Sandbox
  • React Dropzone - Material UI Dropzone: Sandbox

Author​

Antonio Pangallo @antonio_pangall

⭐ Stargazers​

Stargazers repo roster for @iusehooks/usetheform

Contributing​

🎉 First off, thanks for taking the time to contribute! 🎉

We would like to encourage everyone to help and support this library by contributing. See the CONTRIBUTING file.

License​

This software is free to use under the MIT license. See the LICENSE file for license text and copyright information.