npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@radicalbit/formbit

v4.0.0

Published

Lightweight state form library

Readme

Formbit

Formbit is a lightweight React state form library designed to simplify form management within your applications. With Formbit, you can easily handle form state, validate user input, and submit data efficiently.

NPM license

Table of contents

Features

  • Intuitive and easy-to-use form state management.
  • Out of the box support for validation with yup.
  • Full TypeScript generics — useFormbit<FormData>(...) infers paths, values, and callbacks.
  • Support for handling complex forms with dynamic and nested fields via dot-path notation.
  • Context Provider for sharing form state across deeply nested component trees.
  • Seamless and flexible integration with React — works with Antd, MaterialUI, or plain HTML.

Install

npm  install  --save  formbit
yarn  add  formbit

Getting Started

Three steps: define a schema, call the hook, bind the UI.

import * as yup from 'yup';
import useFormbit from '@radicalbit/formbit';

// 1. Define a Yup schema and infer the TypeScript type from it
const schema = yup.object({
  name: yup.string().max(25, 'Max 25 characters').required('Name is required'),
  age:  yup.number().max(120, 'Must be 0–120').required('Age is required'),
});

type FormData = yup.InferType<typeof schema>;

const initialValues: Partial<FormData> = { name: undefined, age: undefined };

// 2. Call the hook with generics so every callback is fully typed
function Example() {
  const { form, submitForm, write, error, isDirty } = useFormbit<FormData>({
    initialValues,
    yup: schema,
  });

  const handleChangeName = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('name', value);
  };

  const handleChangeAge = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('age', Number(value));
  };

  const handleSubmit = () => {
    submitForm(
      ({ form }) => console.log('Validated form:', form),
      ({ errors }) => console.error('Validation errors:', errors),
    );
  };

  // 3. Bind inputs, errors, and submit — Formbit stays out of your UI
  return (
    <div>
      <label htmlFor="name">Name</label>
      <input
        id="name"
        type="text"
        value={form.name ?? ''}
        onChange={handleChangeName}
      />
      <div>{error('name')}</div>

      <label htmlFor="age">Age</label>
      <input
        id="age"
        type="number"
        value={form.age ?? ''}
        onChange={handleChangeAge}
      />
      <div>{error('age')}</div>

      <button disabled={!isDirty} onClick={handleSubmit} type="button">
        Submit
      </button>
    </div>
  );
}

export default Example;

Usage Patterns

Context Provider

Use FormbitContextProvider when you need to share form state across deeply nested components without prop drilling.

import { FormbitContextProvider, useFormbitContext } from '@radicalbit/formbit';
import * as yup from 'yup';

const schema = yup.object({
  name:    yup.string().required('Name is required'),
  surname: yup.string().required('Surname is required'),
  age:     yup.number().required('Age is required'),
});

type FormData = yup.InferType<typeof schema>;

const initialValues: Partial<FormData> = { name: undefined, surname: undefined, age: undefined };

// Wrap your form tree with the provider
function App() {
  return (
    <FormbitContextProvider<FormData>
      initialValues={initialValues}
      yup={schema}
    >
      <NameField />
      <SubmitButton />
    </FormbitContextProvider>
  );
}

// Any child can access form state without props
function NameField() {
  const { form, write, error } = useFormbitContext<FormData>();

  const handleChangeName = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('name', value);
  };

  return (
    <div>
      <input
        value={form.name ?? ''}
        onChange={handleChangeName}
      />
      <span>{error('name')}</span>
    </div>
  );
}

function SubmitButton() {
  const { submitForm, isDirty } = useFormbitContext<FormData>();

  return (
    <button
      disabled={!isDirty}
      onClick={() => submitForm(({ form }) => console.log(form))}
    >
      Submit
    </button>
  );
}

Edit / Initialize Pattern

Start with empty initial values and call initialize() once data arrives from an API.

import { useEffect, useState } from 'react';
import useFormbit from '@radicalbit/formbit';
import * as yup from 'yup';

const schema = yup.object({
  name:  yup.string().required(),
  email: yup.string().email().required(),
});

type FormData = yup.InferType<typeof schema>;

const initialValues: Partial<FormData> = { name: undefined, email: undefined };

function EditUserForm({ userId }: { userId: string }) {
  const { form, write, error, initialize, submitForm } = useFormbit<FormData>({
    initialValues,
    yup: schema,
  });

  const [loading, setLoading] = useState(true);

  // Fetch and initialize — resetForm() will revert to these values
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then((res) => res.json())
      .then((user) => { initialize(user); setLoading(false); });
  }, [userId]);

  const handleChangeName = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('name', value);
  };

  const handleChangeEmail = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('email', value);
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    submitForm(({ form }) => console.log(form));
  };

  if (loading) return <p>Loading...</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input value={form.name ?? ''} onChange={handleChangeName} />
      <div>{error('name')}</div>

      <input value={form.email ?? ''} onChange={handleChangeEmail} />
      <div>{error('email')}</div>

      <button type="submit">Save</button>
    </form>
  );
}

Multi-Step Form

Use __metadata to store step state and validateAll to gate navigation between steps.

import useFormbit from '@radicalbit/formbit';
import * as yup from 'yup';

const schema = yup.object({
  name:  yup.string().required('Name is required'),
  age:   yup.number().required('Age is required'),
  email: yup.string().email().required('Email is required'),
});

type FormData = yup.InferType<typeof schema>;

const initialValues: Partial<FormData> & { __metadata: { step: number } } = {
  name: undefined,
  age: undefined,
  email: undefined,
  __metadata: { step: 0 },
};

function MultiStepForm() {
  const { form, write, error, validateAll, submitForm } = useFormbit<FormData>({
    initialValues,
    yup: schema,
  });

  const step = (form.__metadata?.step as number) ?? 0;
  const goTo = (n: number) => write('__metadata.step', n);

  // Validate only the current step's fields before advancing
  const next = (paths: string[]) => {
    validateAll(paths, {
      successCallback: () => goTo(step + 1),
    });
  };

  const handleChangeName = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('name', value);
  };

  const handleChangeAge = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('age', Number(value));
  };

  const handleChangeEmail = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
    write('email', value);
  };

  const handleSubmit = () => {
    submitForm(({ form }) => console.log('Submit:', form));
  };

  return (
    <div>
      {step === 0 && (
        <div>
          <input value={form.name ?? ''} onChange={handleChangeName} />
          <div>{error('name')}</div>
          <button onClick={() => next(['name'])}>Next</button>
        </div>
      )}

      {step === 1 && (
        <div>
          <input type="number" value={form.age ?? ''} onChange={handleChangeAge} />
          <div>{error('age')}</div>
          <button onClick={() => goTo(0)}>Back</button>
          <button onClick={() => next(['age'])}>Next</button>
        </div>
      )}

      {step === 2 && (
        <div>
          <input value={form.email ?? ''} onChange={handleChangeEmail} />
          <div>{error('email')}</div>
          <button onClick={() => goTo(1)}>Back</button>
          <button onClick={handleSubmit}>Submit</button>
        </div>
      )}
    </div>
  );
}

Local Development

For local development we suggest using Yalc to test your local version of formbit in your projects.

API Reference

FormbitObject

Ƭ FormbitObject<T>: Object

The object returned by useFormbit() and useFormbitContext(). Holds the form state and every method needed to read, mutate and validate the form.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

| Name | Type | Description | | :------ | :------ | :------ | | check | Check<Partial<T>> | Validates json against the current schema; returns the errors, or undefined if valid. | | error | (path: string) => string | undefined | - | | errors | Errors | Error messages registered since the last validation, keyed by the value's dot-path. Example ts form: { age: 1 } errors: { age: "Age must be greater than 18" } | | form | Partial<T> | The current form values. Partial: fields may be missing until validated. | | initialize | Initialize<T> | Re-initializes the form with new initial values. | | isDirty | boolean | True once the user has interacted with the form. | | isFormInvalid | () => boolean | - | | isFormValid | () => boolean | - | | liveValidation | (path: string) => true | undefined | - | | remove | Remove<T> | Removes the value at path, sets isDirty, then validates pathsToValidate plus every live-validated field. | | removeAll | RemoveAll<T> | Removes every given path, sets isDirty, then validates pathsToValidate plus every live-validated field. | | resetForm | () => void | - | | setError | SetError | Sets the error message at path. | | setSchema | SetSchema<T> | Replaces the current validation schema. | | submitForm | SubmitForm<T> | Validates the whole form and, if valid, runs the success callback to submit. | | validate | Validate<T> | Validates only path (ignores live-validated fields). | | validateAll | ValidateAll<T> | Validates only the given paths (ignores live-validated fields). | | validateForm | ValidateForm<Partial<T>> | Validates the whole form and registers any error. | | write | Write<T> | Writes value at path, sets isDirty, then validates pathsToValidate plus every live-validated field. | | writeAll | WriteAll<T> | Writes every [path, value] pair, sets isDirty, then validates pathsToValidate plus every live-validated field. |

Defined in

index.ts:186

Core Types

Errors

Ƭ Errors: Record<string, string>

Error messages registered since the last validation, stored under the same dot-path as the corresponding form value.

Example

form:   { age: 1 }
errors: { age: "Age must be greater than 18" }

Defined in

index.ts:23

FormState

Ƭ FormState<T>: Object

The whole internal state of the form (everything except the validation schema).

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

| Name | Type | | :------ | :------ | | errors | Errors | | form | T | | initialValues | T | | isDirty | boolean | | liveValidation | LiveValidation |

Defined in

index.ts:38

FormbitValues

Ƭ FormbitValues: Record<string, unknown> & { __metadata?: Record<string, unknown> }

Base shape of every form handled by formbit: an open record of values, plus an optional __metadata field formbit uses to carry data that must survive a reset/initialize but must NOT be submitted.

The generic T you pass to useFormbit<T>() must extend this type.

Defined in

index.ts:13

LiveValidation

Ƭ LiveValidation: Record<string, true>

Fields currently under live-validation (re-validated on every form change). A field is added here automatically when it fails a validation. Empty by default.

Example

form:           { age: 1 }
liveValidation: { age: true }

Defined in

index.ts:33

Callback Types

CheckErrorCallback

Ƭ CheckErrorCallback<T>: (json: FormbitValues, inner: ValidationError[], writer: FormState<T>, setError: SetError) => void

Invoked by check() when the given json is invalid.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (json, inner, writer, setError): void

Parameters

| Name | Type | | :------ | :------ | | json | FormbitValues | | inner | ValidationError[] | | writer | FormState<T> | | setError | SetError |

Returns

void

Defined in

index.ts:72

CheckSuccessCallback

Ƭ CheckSuccessCallback<T>: (json: FormbitValues, writer: FormState<T>, setError: SetError) => void

Invoked by check() when the given json is valid.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (json, writer, setError): void

Parameters

| Name | Type | | :------ | :------ | | json | FormbitValues | | writer | FormState<T> | | setError | SetError |

Returns

void

Defined in

index.ts:68

ErrorCallback

Ƭ ErrorCallback<T>: (writer: FormState<T>, setError: SetError) => void

Invoked by validation methods when validation fails.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (writer, setError): void

Parameters

| Name | Type | | :------ | :------ | | writer | FormState<T> | | setError | SetError |

Returns

void

Defined in

index.ts:64

SubmitSuccessCallback

Ƭ SubmitSuccessCallback<T>: (writer: FormState<Omit<T, "__metadata">>, setError: SetError, clearIsDirty: () => void) => void

Invoked by submitForm() once the whole form is valid — the place to send data to the backend. __metadata is stripped from writer.form before this runs.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (writer, setError, clearIsDirty): void

Parameters

| Name | Type | | :------ | :------ | | writer | FormState<Omit<T, "__metadata">> | | setError | SetError | | clearIsDirty | () => void |

Returns

void

Defined in

index.ts:79

SuccessCallback

Ƭ SuccessCallback<T>: (writer: FormState<T>, setError: SetError) => void

Invoked by validation methods when the form (or the validated paths) are valid.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (writer, setError): void

Parameters

| Name | Type | | :------ | :------ | | writer | FormState<T> | | setError | SetError |

Returns

void

Defined in

index.ts:60

Method Types

Check

Ƭ Check<T>: (json: FormbitValues, options?: CheckFnOptions<T>) => ValidationError[] | undefined

See FormbitObject.check.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (json, options?): ValidationError[] | undefined

Parameters

| Name | Type | | :------ | :------ | | json | FormbitValues | | options? | CheckFnOptions<T> |

Returns

ValidationError[] | undefined

Defined in

index.ts:89

Initialize

Ƭ Initialize<T>: (values: Partial<T>) => void

See FormbitObject.initialize.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (values): void

Parameters

| Name | Type | | :------ | :------ | | values | Partial<T> |

Returns

void

Defined in

index.ts:93

Remove

Ƭ Remove<T>: (path: string, options?: WriteFnOptions<T>) => void

See FormbitObject.remove.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (path, options?): void

Parameters

| Name | Type | | :------ | :------ | | path | string | | options? | WriteFnOptions<T> |

Returns

void

Defined in

index.ts:96

RemoveAll

Ƭ RemoveAll<T>: (arr: string[], options?: WriteFnOptions<T>) => void

See FormbitObject.removeAll.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (arr, options?): void

Parameters

| Name | Type | | :------ | :------ | | arr | string[] | | options? | WriteFnOptions<T> |

Returns

void

Defined in

index.ts:116

SetError

Ƭ SetError: (path: string, value: string) => void

See FormbitObject.setError.

Type declaration

▸ (path, value): void

Parameters

| Name | Type | | :------ | :------ | | path | string | | value | string |

Returns

void

Defined in

index.ts:99

SetSchema

Ƭ SetSchema<T>: (newSchema: ValidationSchema<T>) => void

See FormbitObject.setSchema.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (newSchema): void

Parameters

| Name | Type | | :------ | :------ | | newSchema | ValidationSchema<T> |

Returns

void

Defined in

index.ts:102

SubmitForm

Ƭ SubmitForm<T>: (successCallback: SubmitSuccessCallback<T>, errorCallback?: ErrorCallback<Partial<T>>, options?: ValidateOptions) => void

See FormbitObject.submitForm.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (successCallback, errorCallback?, options?): void

Parameters

| Name | Type | | :------ | :------ | | successCallback | SubmitSuccessCallback<T> | | errorCallback? | ErrorCallback<Partial<T>> | | options? | ValidateOptions |

Returns

void

Defined in

index.ts:132

Validate

Ƭ Validate<T>: (path: string, options?: ValidateFnOptions<T>) => void

See FormbitObject.validate.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (path, options?): void

Parameters

| Name | Type | | :------ | :------ | | path | string | | options? | ValidateFnOptions<T> |

Returns

void

Defined in

index.ts:120

ValidateAll

Ƭ ValidateAll<T>: (paths: string[], options?: ValidateFnOptions<T>) => void

See FormbitObject.validateAll.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (paths, options?): void

Parameters

| Name | Type | | :------ | :------ | | paths | string[] | | options? | ValidateFnOptions<T> |

Returns

void

Defined in

index.ts:123

ValidateForm

Ƭ ValidateForm<T>: (successCallback?: SuccessCallback<T>, errorCallback?: ErrorCallback<T>, options?: ValidateOptions) => void

See FormbitObject.validateForm.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (successCallback?, errorCallback?, options?): void

Parameters

| Name | Type | | :------ | :------ | | successCallback? | SuccessCallback<T> | | errorCallback? | ErrorCallback<T> | | options? | ValidateOptions |

Returns

void

Defined in

index.ts:126

Write

Ƭ Write<T>: (path: keyof T | string, value: unknown, options?: WriteFnOptions<T>) => void

See FormbitObject.write.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (path, value, options?): void

Parameters

| Name | Type | | :------ | :------ | | path | keyof T | string | | value | unknown | | options? | WriteFnOptions<T> |

Returns

void

Defined in

index.ts:108

WriteAll

Ƭ WriteAll<T>: (arr: WriteAllValue<T>[], options?: WriteFnOptions<T>) => void

See FormbitObject.writeAll.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

▸ (arr, options?): void

Parameters

| Name | Type | | :------ | :------ | | arr | WriteAllValue<T>[] | | options? | WriteFnOptions<T> |

Returns

void

Defined in

index.ts:112

Options Types

CheckFnOptions

Ƭ CheckFnOptions<T>: Object

Options accepted by check().

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

| Name | Type | | :------ | :------ | | errorCallback? | CheckErrorCallback<T> | | options? | ValidateOptions | | successCallback? | CheckSuccessCallback<T> |

Defined in

index.ts:140

ValidateFnOptions

Ƭ ValidateFnOptions<T>: Object

Options accepted by the validate methods.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Type declaration

| Name | Type | | :------ | :------ | | errorCallback? | ErrorCallback<Partial<T>> | | options? | ValidateOptions | | successCallback? | SuccessCallback<Partial<T>> |

Defined in

index.ts:147

WriteAllValue

Ƭ WriteAllValue<T>: [keyof T | string, unknown]

A single [path, value] pair accepted by writeAll.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Defined in

index.ts:105

WriteFnOptions

Ƭ WriteFnOptions<T>: { noLiveValidation?: boolean ; pathsToValidate?: string[] } & ValidateFnOptions<T>

Options accepted by the write/remove methods (validate options plus path control).

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Defined in

index.ts:154

Yup Re-Exports

ValidateOptions

Ƭ ValidateOptions: YupValidateOptions

Options forwarded to yup's validation methods. See https://github.com/jquense/yup.

Defined in

index.ts:52

ValidationError

Ƭ ValidationError: YupValidationError

The error object yup throws when a validation fails. See https://github.com/jquense/yup.

Defined in

index.ts:55

ValidationSchema

Ƭ ValidationSchema<T>: ObjectSchema<T>

A validation schema built with yup.object(). See https://github.com/jquense/yup.

Type parameters

| Name | Type | | :------ | :------ | | T | extends FormbitValues |

Defined in

index.ts:49

License

MIT © [Radicalbit (https://github.com/radicalbit)]