@radicalbit/formbit
v2.1.0
Published
Lightweight state form library
Downloads
275
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.
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 formbityarn add formbitGetting 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<Values>: Object
Object returned by useFormbit() and useFormbitContextHook(). It contains all the data and methods needed to handle the form.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
| Name | Type | Description |
| :------ | :------ | :------ |
| check | Check<Partial<Values>> | Checks the given json against the form schema and returns an array of errors. It returns undefined if the json is valid. |
| error | (path: string) => string | undefined | - |
| errors | Errors | Object including all the registered error messages since the last validation. Errors are stored using the same path of the corresponding form values. Example If the form object has this structure: json { "age": 1 } and age is a non valid field, errors object will look like this json { "age": "Age must be greater then 18" } |
| form | Partial<Values> | Object containing the updated form. |
| initialize | Initialize<Values> | Initialize the form with new initial values. |
| isDirty | boolean | Returns true if the form is Dirty (user already interacted with the form), false otherwise. |
| isFormInvalid | () => boolean | - |
| isFormValid | () => boolean | - |
| liveValidation | (path: string) => true | undefined | - |
| remove | Remove<Values> | This method updates the form state deleting value, setting isDirty to true. After writing, it validates all the paths contained into pathsToValidate (if any) and all the fields that have the live validation active. |
| removeAll | RemoveAll<Values> | This method updates the form state deleting multiple values, setting isDirty to true. |
| resetForm | () => void | - |
| setError | SetError | Set a message (value) to the given error path. |
| setSchema | SetSchema<Values> | Override the current schema with the given one. |
| submitForm | SubmitForm<Values> | Perform a validation against the current form object, and execute the successCallback if the validation passes, otherwise it executes the errorCallback. |
| validate | Validate<Values> | This method only validates the specified path. Does not check for fields that have the live validation active. |
| validateAll | ValidateAll<Values> | This method only validates the specified paths. Does not check for fields that have the live validation active. |
| validateForm | ValidateForm<Partial<Values>> | This method validates the entire form and sets the corresponding errors if any. |
| write | Write<Values> | This method updates the form state writing $value into the $path, setting isDirty to true. After writing, it validates all the paths contained into $pathsToValidate (if any) and all the fields that have the live validation active. |
| writeAll | WriteAll<Values> | This method takes an array of [path, value] and updates the form state writing all those values into the specified paths. It sets isDirty to true. After writing, it validates all the paths contained into $pathToValidate and all the fields that have the live validation active. |
Defined in
Core Types
Errors
Ƭ Errors: Record<string, string>
Object including all the registered error messages since the last validation. Errors are stored using the same path of the corresponding form values.
Example
If the form object has this structure:
{
"age": 1
}and age is a non valid field, errors object will look like this
{
"age": "Age must be greater then 18"
}Defined in
Form
Ƭ Form: FormbitValues
Object containing the updated form.
Defined in
FormState
Ƭ FormState<Values>: Object
Internal form state storing all the data of the form (except the validation schema).
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
| Name | Type |
| :------ | :------ |
| errors | Errors |
| form | Values |
| initialValues | Values |
| isDirty | boolean |
| liveValidation | LiveValidation |
Defined in
FormbitValues
Ƭ FormbitValues: { __metadata?: FormbitRecord } & FormbitRecord
Base type for form values: a record of string keys with an optional __metadata field.
Defined in
InitialValues
Ƭ InitialValues: FormbitValues
InitialValues used to set up formbit; also used to reset the form to its original version.
Defined in
LiveValidation
Ƭ LiveValidation: Record<string, true>
Object including all the values that are being live validated. Usually fields that fail validation (using one of the methods that triggers validation) will automatically be set to live-validated.
A value/path is live-validated when validated at every change of the form.
By default no field is live-validated.
Example
If the form object has this structure:
{
"age": 1
}and age is a field that is being live-validated, liveValidation object will look like this
{
"age": true
}Defined in
Callback Types
CheckErrorCallback
Ƭ CheckErrorCallback<Values>: (json: Form, inner: ValidationError[], writer: FormState<Values>, setError: SetError) => void
Invoked in case of errors raised by validation of check method.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (json, inner, writer, setError): void
Parameters
| Name | Type |
| :------ | :------ |
| json | Form |
| inner | ValidationError[] |
| writer | FormState<Values> |
| setError | SetError |
Returns
void
Defined in
CheckSuccessCallback
Ƭ CheckSuccessCallback<Values>: (json: Form, writer: FormState<Values>, setError: SetError) => void
Success callback invoked by the check method when the operation is successful.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (json, writer, setError): void
Parameters
| Name | Type |
| :------ | :------ |
| json | Form |
| writer | FormState<Values> |
| setError | SetError |
Returns
void
Defined in
ErrorCallback
Ƭ ErrorCallback<Values>: (writer: FormState<Values>, setError: SetError) => void
Invoked in case of errors raised by validation.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (writer, setError): void
Parameters
| Name | Type |
| :------ | :------ |
| writer | FormState<Values> |
| setError | SetError |
Returns
void
Defined in
SubmitSuccessCallback
Ƭ SubmitSuccessCallback<Values>: (writer: FormState<Values | Omit<Values, "__metadata">>, setError: SetError, clearIsDirty: () => void) => void
Success callback invoked by the submit method when the validation is successful. Is the right place to send your data to the backend.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (writer, setError, clearIsDirty): void
Parameters
| Name | Type |
| :------ | :------ |
| writer | FormState<Values | Omit<Values, "__metadata">> |
| setError | SetError |
| clearIsDirty | () => void |
Returns
void
Defined in
SuccessCallback
Ƭ SuccessCallback<Values>: (writer: FormState<Values>, setError: SetError) => void
Success callback invoked by some formbit methods when the operation is successful.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (writer, setError): void
Parameters
| Name | Type |
| :------ | :------ |
| writer | FormState<Values> |
| setError | SetError |
Returns
void
Defined in
Method Types
Check
Ƭ Check<Values>: (json: Form, options?: CheckFnOptions<Values>) => ValidationError[] | undefined
See FormbitObject.check.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (json, options?): ValidationError[] | undefined
Parameters
| Name | Type |
| :------ | :------ |
| json | Form |
| options? | CheckFnOptions<Values> |
Returns
ValidationError[] | undefined
Defined in
Initialize
Ƭ Initialize<Values>: (values: Partial<Values>) => void
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (values): void
Parameters
| Name | Type |
| :------ | :------ |
| values | Partial<Values> |
Returns
void
Defined in
Remove
Ƭ Remove<Values>: (path: string, options?: WriteFnOptions<Values>) => void
See FormbitObject.remove.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (path, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| path | string |
| options? | WriteFnOptions<Values> |
Returns
void
Defined in
RemoveAll
Ƭ RemoveAll<Values>: (arr: string[], options?: WriteFnOptions<Values>) => void
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (arr, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| arr | string[] |
| options? | WriteFnOptions<Values> |
Returns
void
Defined in
SetError
Ƭ SetError: (path: string, value: string) => void
Type declaration
▸ (path, value): void
Parameters
| Name | Type |
| :------ | :------ |
| path | string |
| value | string |
Returns
void
Defined in
SetSchema
Ƭ SetSchema<Values>: (newSchema: ValidationSchema<Values>) => void
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (newSchema): void
Parameters
| Name | Type |
| :------ | :------ |
| newSchema | ValidationSchema<Values> |
Returns
void
Defined in
SubmitForm
Ƭ SubmitForm<Values>: (successCallback: SubmitSuccessCallback<Values>, errorCallback?: ErrorCallback<Partial<Values>>, options?: ValidateOptions) => void
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (successCallback, errorCallback?, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| successCallback | SubmitSuccessCallback<Values> |
| errorCallback? | ErrorCallback<Partial<Values>> |
| options? | ValidateOptions |
Returns
void
Defined in
Validate
Ƭ Validate<Values>: (path: string, options?: ValidateFnOptions<Values>) => void
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (path, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| path | string |
| options? | ValidateFnOptions<Values> |
Returns
void
Defined in
ValidateAll
Ƭ ValidateAll<Values>: (paths: string[], options?: ValidateFnOptions<Values>) => void
See FormbitObject.validateAll.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (paths, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| paths | string[] |
| options? | ValidateFnOptions<Values> |
Returns
void
Defined in
ValidateForm
Ƭ ValidateForm<Values>: (successCallback?: SuccessCallback<Values>, errorCallback?: ErrorCallback<Values>, options?: ValidateOptions) => void
See FormbitObject.validateForm.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (successCallback?, errorCallback?, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| successCallback? | SuccessCallback<Values> |
| errorCallback? | ErrorCallback<Values> |
| options? | ValidateOptions |
Returns
void
Defined in
Write
Ƭ Write<Values>: (path: keyof Values | string, value: unknown, options?: WriteFnOptions<Values>) => void
See FormbitObject.write.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (path, value, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| path | keyof Values | string |
| value | unknown |
| options? | WriteFnOptions<Values> |
Returns
void
Defined in
WriteAll
Ƭ WriteAll<Values>: (arr: WriteAllValue<Values>[], options?: WriteFnOptions<Values>) => void
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
▸ (arr, options?): void
Parameters
| Name | Type |
| :------ | :------ |
| arr | WriteAllValue<Values>[] |
| options? | WriteFnOptions<Values> |
Returns
void
Defined in
Options Types
CheckFnOptions
Ƭ CheckFnOptions<Values>: Object
Options object to change the behavior of the check method.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
| Name | Type |
| :------ | :------ |
| errorCallback? | CheckErrorCallback<Values> |
| options? | ValidateOptions |
| successCallback? | CheckSuccessCallback<Values> |
Defined in
ValidateFnOptions
Ƭ ValidateFnOptions<Values>: Object
Options object to change the behavior of the validate methods.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Type declaration
| Name | Type |
| :------ | :------ |
| errorCallback? | ErrorCallback<Partial<Values>> |
| options? | ValidateOptions |
| successCallback? | SuccessCallback<Partial<Values>> |
Defined in
WriteAllValue
Ƭ WriteAllValue<Values>: [keyof Values | string, unknown]
Tuple of [key, value] pair.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Defined in
WriteFnOptions
Ƭ WriteFnOptions<Values>: { noLiveValidation?: boolean ; pathsToValidate?: string[] } & ValidateFnOptions<Values>
Options object to change the behavior of the write methods.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Defined in
Yup Re-Exports
ValidateOptions
Ƭ ValidateOptions: YupValidateOptions
Type imported from the yup library. It represents the object with all the options that can be passed to the internal yup validation method.
Link to the Yup documentation https://github.com/jquense/yup
Defined in
ValidationError
Ƭ ValidationError: YupValidationError
Type imported from the yup library. It represents the error object returned when a validation fails.
Link to the Yup documentation https://github.com/jquense/yup
Defined in
ValidationSchema
Ƭ ValidationSchema<Values>: ObjectSchema<Values>
Type imported from the yup library. It represents any validation schema created with the yup.object() method.
Link to the Yup documentation https://github.com/jquense/yup
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Defined in
Deprecated Types
ClearIsDirty
Ƭ ClearIsDirty: () => void
Deprecated
Inlined into FormbitObject.
Type declaration
▸ (): void
Returns
void
Defined in
ErrorCheckCallback
Ƭ ErrorCheckCallback<Values>: CheckErrorCallback<Values>
Deprecated
Use CheckErrorCallback instead.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Defined in
ErrorFn
Ƭ ErrorFn: (path: string) => string | undefined
Deprecated
Inlined into FormbitObject.
Type declaration
▸ (path): string | undefined
Parameters
| Name | Type |
| :------ | :------ |
| path | string |
Returns
string | undefined
Defined in
IsDirty
Ƭ IsDirty: boolean
Deprecated
Inlined into FormbitObject.
Defined in
IsFormInvalid
Ƭ IsFormInvalid: () => boolean
Deprecated
Inlined into FormbitObject.
Type declaration
▸ (): boolean
Returns
boolean
Defined in
IsFormValid
Ƭ IsFormValid: () => boolean
Deprecated
Inlined into FormbitObject.
Type declaration
▸ (): boolean
Returns
boolean
Defined in
LiveValidationFn
Ƭ LiveValidationFn: (path: string) => true | undefined
Deprecated
Inlined into FormbitObject.
Type declaration
▸ (path): true | undefined
Parameters
| Name | Type |
| :------ | :------ |
| path | string |
Returns
true | undefined
Defined in
Object
Ƭ Object: FormbitRecord
Deprecated
Use FormbitRecord instead. Renamed to avoid shadowing the global Object.
Defined in
ResetForm
Ƭ ResetForm: () => void
Deprecated
Inlined into FormbitObject.
Type declaration
▸ (): void
Returns
void
Defined in
SuccessCheckCallback
Ƭ SuccessCheckCallback<Values>: CheckSuccessCallback<Values>
Deprecated
Use CheckSuccessCallback instead.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Defined in
Writer
Ƭ Writer<Values>: FormState<Values>
Deprecated
Use FormState instead.
Type parameters
| Name | Type |
| :------ | :------ |
| Values | extends InitialValues |
Defined in
License
MIT © [Radicalbit (https://github.com/radicalbit)]
