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

react-forms-system

v1.1.5

Published

A react forms library

Readme

React Forms System

NOW REACT NATIVE COMPATABLE!!

Yet another react forms library.... Don't worry there are some new ideas as well as refined ideas from other libraries that will make your life so much easier.

Why use this package?

React Forms System is a simple forms state management library that provides powerful validation api. The biggest perk is that it does not get into the game of building out its own UI, and instead relies heavily on the inversion of control pattern. Things like component implementation, validation messages and validators are left up to the user to implement. This inversion of control allows virtually any UI components from any library to be used with this library. In summary, React Forms System tries to do only a few things but do them well.

What sets this apart?

The biggest thing that sets this library apart is how expressive the built in validation states are and the built in ability to validate using values from other control values within the form. The validation states are heavily influenced by angular form validation which gives an amazing amount of flexibility to control not only if there are validation errors but if the user should be blasted by validation messages before they have even had an opportunity to interact with the form.

A second big thing that sets this library apart is its Control component and how simple and flexible the api is. The Control component definitely leans on the BYOC (bring your own component) paradigm. The api is neat because it tells you what the value of your control is and its validation state and in return your control only needs to tell the form when the value changes and optionally when it should be considered touched.

Contents

Features

  • Minimal library components
  • Control level and form level validation
  • Managed or unmanaged state
  • Dirty, touched and pending states
  • Customizable controls
  • Built in peer control evaluation
  • React Web and React Native compatible

Getting Started

Prerequisites

React

$ npm i react

Installation

$ npm i react-forms-system

React Native

The only requirement to get this to work for react native is to pass a different component prop to the Form with a react native compatable component. View should do just fine (and is really the only thing I have tested up to this point)

import { View } from 'react-native';
...
<Form onSubmit={...} component={View} >
...
</Form>

Components

Form

<Form /> is a HOC that manages and maintains the state of all the <Control /> that are nested below it.

Props

| Prop | Type | Description | | ------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | onSubmit | (FormState)=>void | onSubmit is called any time the html form submit event is called. It passes the entire state of the form as its only argument. | | onStateChange | (FormState)=>void | onStateChange is called every time there is a state update. It passes the entire state of the form as its only argument. | | component | React.Component | If you want to use a different underlying component for the form besides and html <form /> then pass it here. View is the only react native component that has been tested with this prop atm. | | ... | ... | All other props passed will be directly applied to the underlying html <form /> component or the passed component. |

Basic Form
<Form onSubmit={(formState) => console.log(formState)}>
  // ... form content here
</Form>
Form State

The form state can very based off your Controls, but the root of the form state always stays the same:

{
    values: { ... }, // A map of the Forms Control values
    validationState: { ... } // A map of Forms Control validation states
}
Submit Source

The second argument passed to the onSubmit callback will be the submit source. In the case of an html form submit, the value will be native_submit. In the case of a custom submit control, it will be the name of the control.

import CustomSubmit from './some/path/CustomSubmit';

// Should log 'native_submit'
<Form onSubmit={(formState, source) => console.log(source)}>
  <button type="submit">Submit</button>
</Form>

// Should log 'customSubmit'
<Form onSubmit={(formState, source) => console.log(source)}>
  <Form.Control name="customSubmit" component={CustomSubmit} submit />
</Form>

Control

A <Control /> is a HOC that wraps and provides all <Form /> interaction to any controls you would like to use in your form.

Props

| Prop | Type | Description | | ---------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name | string | The name to call the component | | component | React.Component | The component to use as a control | | value | any | The current value of the control when using in a managed form | | defaultValue | any | The initial value of the control | | validators | {} | A map of validators to be apply to this control on value change | | peerDependencies | {} | A list of peer <Control /> components names whos values will need to be used in one or more of the controls validators. | | isValidCheck | (validationState)=>boolean | A predicate that will be used to determine the overall valid key of the controls validationState. The default validator considers any false validation values to be considered valid: false | | submit | bool | If true this control will now be treated as a submittable. value, defaultValue, validators, peerDependencies and isValidCheck will no longer have any effect when this is enabled. | | ... | ... | All other props will be forwarded to the control wrapper passed in component prop. |

Basic Control
// Common use for a unmanaged form
// Must use `defaultValue` prop for unmanaged form controls
<Form.Control
    name="myControlName"
    component={MyControlComponent}
    defaultValue=""
/>

// Common use for a managed form
<Form.Control
    name="myControlName"
    component={MyControlComponent}
    value=""
/>
Wrapper Interface

An example of the props that are passed by the Control component.

const MyControlComponent = ({ name, value, onChange, onTouch, validationState }) = {
    return (
 		// ...wrapped component to be used as a control goes here
    );
};

See Control Wrapper for example

Validator

A validator is a function that takes a control value and a map of peer values and returns true for passed validation and false for failed validation. A validator can also return a Promise as every single validator gets wrapped in a Promise.all() during evaluation.

(value, peerValues) => !!value || !!peerValues.peer;

The format for passing validators to the control is:

<Form.Control
    ...
    validators={{ myValidator: (value, peerValues) => ... }}
    ...
/>

or for making validators reusable

const myValidator = (value, peerValues) => ...;
...
<Form.Control
    ...
    validators={{ myValidator }}
    ...
/>

Out of the box, the key valid will be set in the validationState of each control as true if all validators were true or false if any validator returned false. You can override this by passing the isValidCheck prop. isValidCheck receives the controls validationState and returns a true if field should be considered valid and false if it shouldn't. Here is a simple example:

const required = (value) => !!value;
...
<Form.Control
    ...
    validators={{ required }}
    isValidCheck={(validationState) => !!validationState.required }
    ...
/>

See Basic Validation for example

Peer Dependencies

Peer dependencies are any other <Control /> component that the current control will need values for to run its own validation. The format for passing peer dependencies is:

<Form.Control
    ...
    peerDependencies={{ nameOfPeerControl: 'aliasForPeerControl'}
    ...
/>

See Cross control validation for an example

Submit

Controls can be turned into a submittable by adding the prop submit. Any control that has the prop submit will no longer send value, onChange, onTouch or validationState. Instead it will recieve the callback onSubmit which will trigger a form submit. In addition the

const CustomSubmit = ({ onSubmit }) => (<button onClick={onSubmit}>Submit</button>);
...
<Form.Control
	name="custom_submit"
	component={CustomSubmit}
	submit
/>

Examples

Managed Form

A managed form lets you manage the state of the form yourself

import React from "react";
import { Form } from "react-forms-system";
import { NameInputComponent } from "../somewherelocal/NameInputComponent";

class ManagedForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { firstName: "", lastName: "" };
  }

  render() {
    return (
      <Form onStateChange={(formState) => this.setState(formState.values)}>
        <Form.Control
          name="firstName"
          component={NameInputComponent}
          value={this.state.firstName}
        />
        <Form.Control
          name="lastName"
          component={NameInputComponent}
          value={this.state.lastName}
        />
      </Form>
    );
  }
}

Unmanaged Form

An unmanaged form manages its own state internally. Notice that all controls have a defaultValue which is required for unmanaged forms.

import React from "react";
import { Form } from "react-forms-system";
import { NameInputComponent } from "../somewherelocal/NameInputComponent";

const UnmanagedForm = () => {
  return (
    <Form onSubmit={console.log}>
      <Form.Control
        name="firstName"
        component={NameInputComponent}
        defaultValue=""
      />
      <Form.Control
        name="lastName"
        component={NameInputComponent}
        defaultValue=""
      />
    </Form>
  );
};

Control Wrapper

Each control used in the form must be wrapped in a HOC. The HOC will recieve props from the form and allow the underlying control to render itself appropriately. In addition, any props passed to the <Form.Control /> component will be passed strait through the HOC to the underlying control, as long as they don't conflict with the <Form.Control /> API. The underlying component is then also required to apply its own value value and fire off events when that value has changed (onChange) as well as to signify a touch of the component (onTouch).

// NameInputComponent.jsx
import React from "react";

export default ({ value, onChange, onTouch }) => {
  return (
    <input
      value={value}
      onChange={(e) => onChange(e.target.value)}
      onClick={(e) => onTouch()}
    />
  );
};

Basic Validation

Each control is passed a set of validation states (validationState) as a prop, which can be used to render itself appropriately for validation states. The inherent members of validationState are dirty, touched, pending and valid.

dirty - The control has fired the onChange event

touched - The control has fired the onTouch event

pending - One or more of the validators is still running.

valid - All the validators return true or isValidCheck callback returns true

These four validation states are also available globally to the whole form directly under the validationState key. a.k.a. validationState.dirty expresses whether or not the form is dirty. The meanings for the form level keys are the same.

// NameInputComponent.jsx
import React from 'react';

export default ({ value, onChange, onTouch, validationState }) => {
    const className = (validationState.touched || validationState.dirty) &&
          validationState.valid ? '' : 'invalid';
    return (
        <input
            className={className}
            value={value}
            onChange={(e) => onChange(e.target.value)}
            onClick={(e) => onTouch()}
        />
    );
};

// Form.jsx
import React from 'react';
import { Form } from 'react-forms-system';
import { NameInputComponent } from 'NameInputComponent';

const required = (value) => !!value;
// validAlwaysFalse will force valdiationState.valid to be false for any control that uses it, in this example its being used on lastName control
const validAlwaysFalse => (validationState) => false;

const UnmanagedForm = () => {
   	return (
        <Form onSubmit={console.log} >
            <Form.Control
                name="firstName"
                component={NameInputComponent}
                defaultValue={''}
                validators={{ required }}
            />
            <Form.Control
                name="lastName"
                component={NameInputComponent}
                defaultValue={''}
                isValidCheck={validAlwaysFalse}
                validators={{ required }}
            />
        </Form>
    );
};

Cross Control Validation

You can pass the prop peerDependencies to a control to get it to include a peer controls value in any validators it runs. The following example will only validate the location and provider fields if one of them has a value. The format for passing peerDependencies is:

{{ 'control name': 'validator alias name' }}

control name - The name of the peer control

validator alias name - the key name you want the value of the target control to be referenced as under in peerValues of the validator.

// Form.jsx
import React from "react";
import { Form } from "react-forms-system";
import { NameInputComponent } from "NameInputComponent";

const myselfOrPeer = (value, peerValues) => !!value || !!peerValues.peer;

const UnmanagedForm = () => {
  return (
    <Form onSubmit={console.log}>
      <Form.Control
        name="provider"
        component={NameInputComponent}
        defaultValue={""}
        validators={{ myselfOrPeer }}
        peerDependencies={{
          location: "peer",
        }}
      />
      <Form.Control
        name="location"
        component={NameInputComponent}
        defaultValue={""}
        validators={{ myselfOrPeer }}
        peerDependencies={{
          provider: "peer",
        }}
      />
    </Form>
  );
};

Related Projects

Here is a list of other react form projects that are similar in purpose to react-forms-system.

  • https://www.npmjs.com/package/informed
  • https://www.npmjs.com/package/formik

Roadmap

  • Better error messages for poor use of api
  • debounce state updates
  • Test coverage
  • Optimize Depencencies
  • Hosted docs
  • Bundle optimization
  • Supporting package with prebuilt controls
  • Supporting package with common validators
  • More examples