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

@paros-ui/react-forms

v3.1.6

Published

Yet another React form library

Readme

@paros-ui/react-forms

On the web, forms exist everywhere. Here is yet another way to write React forms, vaguely inspired by mobx and redux-form / react-final-form

npm version

Building a Form

The first step is to initialize your form object. It will be in charge of state, validation, and event handling.

Once initialized, you can render the Form component and any number of Fields. Their name is specified by accessing properties of the Fields object

import React from "react";
import ReactForm from "@paros-ui/react-forms";

import LoginSchema from "./schemas/login";

class TestForm extends React.Component {
    form = new ReactForm({
        initialValues: {
            rememberMe: true
        }
    });

    onSubmit = data => {
        console.log(data); //{ "username": "...", "password": "...", "rememberMe": true }
    };

    render() {
        let { Form, Fields } = this.form;

        return (
            <Form onSubmit={this.onSubmit}>
                {/* This is a field called "username" */}
                <Fields.username component="input" type="email" placeholder="Email address" />
                {/* This is a field called "password" */}
                <Fields.password component="input" type="password" placeholder="Password" />

                {/* This is a field called "rememberMe" */}
                <Fields.rememberMe template="checkbox" component="input" />
                <label for="rememberMe">Remember Me</label>

                <button type="submit">Login</button>
            </Form>
        );
    }
}

The Form component will automatically intercept the submit event, perform validation (if configured), and call your onSubmit with the validated form data. If you use joi, it will provide the sanitized value from schema.validate(), otherwise it will be the raw form state.

Fields is a JavaScript proxy, like a dynamic object. When you reference a property of it, it'll generate and cache a wrapper component linked to that form, where the name of that field is the name of the property you are accessing. In the above example, it is caching three components and initializing the form state with three values, username, password and rememberMe. On blur, it will validate that field. On change, will reset the validation status for that field. It will also pass onBlur and onChange events through after it handles them internally.

Using a React Hook

If you prefer to use hooks, a useForm hook is available. It is just a wrapper around useState to manage a reference to ReactForm, making the hook functionally identical. All documentation will demonstrate with the ReactForm class for consistency, but can be implemented the same way using the useForm hook.

import React from "react";
import useForm from "@paros-ui/react-forms/hooks/useForm";

const onSubmit = (values, form) => {
    // The second parameter gives you access to the ReactForm object
    console.log(data); //{ "username": "...", "password": "...", "rememberMe": true }
};

let form = {
    initialValues: {
        rememberMe: true
    }
};

function Component(props) {
    let { Form, Fields } = useForm(form);

    return (
        <Form onSubmit={onSubmit}>
            <Fields.username component="input" type="email" placeholder="Email address" />
            <Fields.password component="input" type="password" placeholder="Password" />

            <Fields.rememberMe template="checkbox" component="input" />
            <label for="rememberMe">Remember Me</label>

            <button type="submit">Login</button>
        </Form>
    );
}

Default Values

Initial values can be passed into the ReactForm options object, or the <Form /> component.

The <Form /> component has higher priority, so if both are specified, it will defer to that.

let { Form } = new ReactForm({
    initialValues: {
        rememberMe: true
    }
});

<Form initialValues={{ rememberMe: false }}>
    ...
</Form>

Validation

Validation is supported via both joi object schemas and custom validator functions. The validator can be passed into the ReactForm options object, or like with the initialValues it can be passed as a prop to <Form />:

import joi from "joi";

const schema = joi.object({
    username: joi.string().email({ tlds: { allow: false } }).required(),
    password: joi.string().min(6).max(42).required()
});

let { Form } = new ReactForm({
    validation: schema
});

<Form validation={schema}>
    ...
</Form>

Alternatively, for fine-grain control over the validation process, you can pass a function as the validation prop:

let form = new ReactForm({
    validation: (data, errors) => {
        if(data.password !== "secret")
            errors.password = "Invalid password";
    }
});

The validation function can even be asynchronous!

let form = new ReactForm({
    validation: async(data, errors) => {
        if(!(await checkToken(data.token)))
            errors.token = "Invalid token";
    }
});

Validation on Blur

When a joi schema is used as validation or is not explicitly configured, when a field blur event fires, it will automatically validate the field. If a function validator is used, or validateOnBlur disabled either at the field or form level, it will disable this functionality.

let { Fields } = new ReactForm({
    validateOnBlur: false // Disable at the form level
});

// Enable for this specific field
<Fields.username component="input" type="text" placeholder="Username" validateOnBlur />

Simple Inputs

For most projects, you don't use a super complex text input for usernames, passwords, checkboxes, etc.

For this reason, there are a number of templates available. They will autofill certain properties when hooking into the component so you can write less code. Simply provide a template property with the desired template name, and it will take care of the rest.

Checkboxes

const Checkbox = ({ label, id, ...props }) => (
    <div className="form-group">
        <input type="checkbox" id={id} {...props} />
        <label for={id}>{label}</label>
    </div>
);

// Without using the template.
<Fields.rememberMe component={Checkbox} valueProp="checked" valueGetter="target.checked" label="Remember me" />

// With the `checkbox` template
<Fields.rememberMe template="checkbox" component={Checkbox} label="Remember me" />

Adding Templates

When you want to create reusable form components, you don't need to import it everywhere. Instead, you can extend the form system's built-in templating.

import { addTemplates } from "@paros-ui/react-forms/templates";

addTemplates({
    "password": ({ output, template, templateProps }) => {
        if(!output.Component) {
            output.Component = "input";
        }

        templateProps.type = "password";
        template.forbidNull = true;
    }
});

<Fields.fieldName template="password" placeholder="Enter a password" />

Complex Inputs

Most of the time, the above will cover most use cases. However, sometimes your inputs are more complex than standard HTML inputs, and/or have more complex data. For this, we can use valueProp, valueGetter, and template to customize exactly how the form interfaces with your inputs.

valueProp (default: "value")

This will determine how the form will set the value property on the component. For checkboxes, valueProp needs to be "checked", because this is the property React expects for setting the value.

valueGetter (default: "target.value")

This determines how to extract the value from the onChange event. For checkboxes, valueGetter needs to be "target.checked". valueGetter can be a string or a synchronous function.

<Fields.fieldName component="input" type="checkbox" valueProp="checked" valueGetter="target.checked" />
<Fields.fieldName component="input" type="checkbox" valueProp="checked" valueGetter={e => e.target.checked} />

ArrayFieldAdapter

Sometimes, your form utilizes an array of malleable data. For this, the ArrayFieldAdapter was created.

Give it a keyProp for your data, and a body to render, and it will automatically track changes without updating every item. Very performant!

The function you specify can have three parameters, item, props and index.

props has the value, error, onChange, etc. for that specific item. This gives you complete control over how you render each item.


const ComplexComponent = ({
    item, value, onChange
}) => (
    <div className="form-group">
        <label>{item.name}</label>
        <input type="phone"
            className="form-control"
            placeholder="Enter Phone Number"
            value={value}
            onChange={onChange} />
    </div>
);

<Fields.phoneNumbers template="array" keyProp="id">
    {(item, props) => (
        <ComplexComponent item={item} {...props}>
    )}
</Fields.phoneNumbers>

Complex Data Structures

Forms are rarely always a flat object. Utilities like the FormObject were created just for this.

FormObject

Nesting a form component like a Field inside of a FormObject will change the path to its value.

Take this data structure as an example:

{
    "email": "",
    "creditCard": {
        "number": "",
        "exp": "",
        "cvv": ""
    }
}

You could implement the form with no additional effort by utilizing a FormObject wrapper:


<Fields.email template="text" type="email" />

<FormObject field="creditCard">
    <Fields.number template="number" format="####-####-####-####" />
    <Fields.exp template="number" format="##/##" />
    <Fields.cvv template="number" format="###" />
</FormObject>

Monitoring, Field Dependencies and Reactions

Sometimes, complex form interactions between fields are necessary. Because of this, some advanced components and utilities have been implemented.

FieldWatcher

The most simple monitor is a FieldWatcher. Give it a field or fields prop and it'll render a child function when those field(s) change. The parameter given to the function is an object with the keys being each watched field.

import ReactForm from "@paros-ui/react-forms";

let { Form, Fields, FieldWatcher } = new ReactForm({ ... });

<FieldWatcher field="testFieldName">
    {({ testFieldName }) => (
        // Your JSX here
    )}
</FieldWatcher>

<FieldWatcher fields={[ "fieldA", "fieldB" ]}>
    {({ fieldA, fieldB }) => (
        // Your JSX here
    )}
</FieldWatcher>

Monitors

Fields sometimes need to reference values from other fields in the same form. To accomplish this, simply add a monitors prop to a field, and a prop will be injected with its value. Additionally, the component will update when the monitored values change. monitors behaves like both field and fields from FieldWatcher, so you can specify either an array or a single field.


const EndDate = ({ startDate, value, onChange }) => (
    <DatePicker minDate={startDate} value={value} onChange={onChange} />
);

const OptionsDropdown = ({ startDate, endDate, ...props }) => (
    <AvailableOptionsDropdown startDate={startDate} endDate={endDate} {...props} />
);

<Fields.startDate template="datepicker" />
<Fields.endDate template="datepicker"
    monitors="startDate"
    component={EndDate} />
<Fields.options component={OptionsDropdnw} monitors={[ "startDate", "endDate" ]}>

fieldRequires and fieldExcludedBy

Sometimes there is a hard relationship between two fields. One requires another be populated, or one may entirely disable another. These props for fields enable such relationships.

fieldRequires will automatically disabled the inner component if the specified field is not populated.

fieldExcludedBy will automatically disabled the inner component if the specified field is populated.

Updating Form State

Using functions built into the form object, you can update and fetch information about the form at any time. Performing updates will trigger updates in affected fields.

Get/Set Values

let form = new ReactForm({
    initialValues: {
        rememberMe: false
    }
});

// Fetching a single value
let rememberMe = form.value("rememberMe"); // false

// Fetching multiple values
let [ username, password ] = form.values("username", "password");

// Fetching all values
let { username, password } = form.values();

// Setting a single value
form.value("rememberMe", true);

// Setting multiple values
form.values({
    rememberMe: true
});

Get/Set Errors

let form = new ReactForm({
    initialValues: {
        rememberMe: false
    }
});

// Fetching a single error
let rememberMe = form.error("rememberMe"); // null

// Fetching multiple errors
let [ username, password ] = form.errors("username", "password");

// Fetching all errors
let { username, password } = form.errors();

// Setting a single error
form.error("rememberMe", "Form error");

// Setting multiple errors
form.errors({
    rememberMe: "Form error"
});

Get Form State

let form = new ReactForm({
    initialValues: {
        rememberMe: false
    }
});

form.value("rememberMe", true);

// Get all values
let values = form.values();
// Get all errors
let errors = form.errors();

// Or get all errors and values together
let { errors, values } = form.getState();
console.log(values.rememberMe, errors.rememberMe); // true, undefined

Reset a Form to Initial Values

let form = new ReactForm({
    initialValues: {
        rememberMe: false
    }
});

form.value("rememberMe", true);
form.resetForm(); //Resets both values and errors
form.resetValues(); //Resets values
form.resetErrors(); //Resets errors

let value = form.value("rememberMe"); // false

Reset Form Errors

let form = new ReactForm({
    initialValues: {
        rememberMe: false
    }
});

(UNSAFE) Replacing Values / Errors

If you want to force-set values or errors, and not update the form in any way, it can be achieved with the following utility functions. It is not recommended to use.

let form = new ReactForm({
    initialValues: {
        username: "test_user",
        password: "Passw0rd!"
    }
});

form.replaceValues({
    username: "",
    password: ""
});

form.replaceErrors({
    username: "Invalid username",
    password: "Invalid password"
});

If you pass true as a second parameter to replaceValues, it will also set the initialValues to the specified object

Manually Triggering Validation and Updating Components

The form object provides three utility functions for updating components or triggering validation.

let form = new ReactForm({
    validation: (values, errors) => {
        if(!values.username)
            errors.username = "Username is required";
    }
});

let { Form, Fields } = form;

<Form>
    <Fields.username component="input" type="text" placeholder="Username" />
</Form>

form.validate(); // Will validate the entire form
form.validate("username"); // Will activate the validator and update the component
form.change("username"); // Will trigger the general change and update events, and update the field
form.update("username"); // Will trigger the field-specific change event, updating the field

Changing the Validator at any Time

The validator can be changed dynamically by accessing the validation property of the form object.

let form = new ReactForm({
    validation: () => null
});

// Future validation will use the new function / schema
form.validation = joi.object();

Internal Events

ReactForm extends a facsimile of the Node.js EventEmitter, so you can subscribe to any internal event or manually emit events with the familiar .on(), .off(), .emit(), etc. functions.

Additionally, when initializing ReactForm, you can optionally specify a submit and/or change handler. You can do the same when rendering the <Form /> component:

let onChange = ({ field, value }) => {};
let onSubmit = values => {};

let { Form } = new ReactForm({
    onChange: onChange,
    onSubmit: onSubmit
});

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

"change"

Change is fired when any field's value or error changes.

let form = new ReactForm();

// Using helper function
form.onChange(({ field, value }) => {
    console.log(`Field "${field}" has value ${value}`);
});

// Using generic event subscription
form.on("change", ({ field, value }) => {
    console.log(`Field "${field}" has value ${value}`);
});

"update"

Update is fired when the form updates, from validation to field value change to form submit.

let form = new ReactForm();

// Using generic event subscription
form.on("update", ({ errors, values }) => {
    ...
});

"submit"

Fired when the form passes validation and is ready to be processed externally. This event is used to fire any onSubmit provided.

let form = new ReactForm();

// Using helper function
form.onSubmit(values => {
    // ...
});

// Using generic event subscription
form.on("submit", values => {
    // ...
});

"errors"

Fired when the form submits but fails validation.

let form = new ReactForm();

// Using generic event subscription
form.on("errors", errors => {
    // ...
});

"change-{field name}"

Fired immediately after the generic "change" event, but this allows you to listen to a specific field's updates.

let form = new ReactForm();

// Using generic event subscription
form.on("change-username", (value, error) => {
    // ...
});

<Fields /> Utilities and Props

The <Fields /> component will pass all props it receives to its component, including refs, but there are a few that will dictate behavior and not be passed through.

component

This determines how to actually render the field.

template

This will let you choose a template to be used internally by the field component builder to autofill some properties. The following templates come built-in, and can be extended/overridden:

  • "component" - Only adds a default for the valueGetter, using the direct onChange value.
  • "text" - Defaults to <input type="text" /> and forbids null values, forcing the form value to at least be ""
  • "textarea" - Same as text template, just uses <textarea /> instead.
  • "checkbox" - Defaults to <input type="checkbox">, sets default value to false, and sets defaults for valueGetter / valueProp
  • "select" - Sets some convenient defaults for a react-select integration. Requires extending to provide the default component.
  • "array" - Uses the ArrayFieldAdapter

defaultValue

Behaves similarly to initialValues when initializing the form, but this allows you to set it on a per-field basis instead of specifying all at once with initialValues. Has priority over form-level initialValues setting.

validateOnBlur

Behaves similarly to validateOnBlur when initializing the form, but this allows you to toggle validation on a per-field basis. Has priority over form-level setting.

name

Since all fields are controlled components, you shouldn't need to care about the name property, and it'll by default be filtered out. If you absolutely must set it, use the componentProps property defined below.

componentProps

If for some reason you want to use some of these property names for the inner component, you can specify them in a componentProps object.

<Form /> Utilities and Props

<Form /> will pass refs to the internal <form /> component.

ReactForm Utilities and Props

debugUpdates (default: false)

This will log any update events to the console.

debugChanges (default: false)

This will log any change events to the console.

onError

This optional property expects a function, and will subscribe to the errors event that fires when a form submits but fails validation.

onUpdate

This optional property expects a function, and will subscribe to the update event that fires when a form changes state at all.

name (default: "form")

Currently unused except for debugUpdates and debugChanges to prefix update/change logging.