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

minimal-form-data-hoc

v3.0.1

Published

A minimalistic HOC for typed form data in React

Readme

minimal-form-data-hoc (aka. miniform)

This package exposes a minimalistic higher-order component for typed form data. The implementation consists of <50 lines of code, with a key focus on simplicity. You are encouraged to copy and modify the source directly if the component does not fit your exact needs.

Installation

The package is available in the npm registry. Install using your favorite package manager, e.g.

npm install minimal-form-data-hoc

Usage

The higher-order component is used by first defining a form scheme with the following structure:

import miniform from 'minimal-form-data-hoc';

const scheme = miniform.createFormScheme(() => ({
  username: {
    value: 'Initial value',
    rules: [
      ['Please fill required value', (value: string) => !!value],
      ['Input too short', (value: string) => value.length < 5],
    ],
  },
}));

The scheme is then passed on to the withFormData HOC and used to construct the initial state of the form data. Note that the createFormScheme method is simply a typed identity function to help define schemes with correct type. Each property in the scheme is mapped to an object with a value, an error (if any of the validation rules return true) and an onChange method used to update the state of the form data. The following shows a minimal example with a single text field:

const LoginForm = miniform.withFormData(scheme)(props => {
  return (
    <form>
      Username: <input value={props.username.value} onChange={props.username.onChange} />
      <span>{props.username.error}</span>
    </form>
  );
});

Dynamic initial values and typed props

Note that the withFormData higher-order component's first argument is in fact a function. This function's input argument is the wrapped component's props which can be used to provide dynamic initial values. Consider the following example:

interface IProps {
  user: {
    name: string
  }
}

const scheme = miniform.createFormScheme((props: IProps) => ({
  username: { value: props.user.name },
}));

const LoginForm = miniform.withFormData(scheme)(props => {
  ...
});

This allows us to use the component's props for initial values. Furthermore, by providing a type for the input argument, the props in the wrapped component is also correctly typed.

Examples


Text fields

Username:

Password:

Text fields simply pass on the values from the form data.

const scheme = miniform.createFormScheme(() => ({
  username: { value: '' },
  password: { value: '' },
}));

const InputForm = miniform.withFormData(scheme)(props => {
  return (
    <form>
      Username:<br/><input value={props.username.value} onChange={props.username.onChange} /><br/>
      Password:<br/><input value={props.password.value} onChange={props.password.onChange} />
    </form>
  );
});

export default InputForm;

Standalone checkbox

:ballot_box_with_check: Checkbox

A standalone checkbox uses a boolean scheme property value, which is passed on to the input element's checked property.

import withFormData from 'minimal-form-data-hoc';

const scheme = miniform.createFormScheme(() => ({
  consent: { value: false },
}));

const InputForm = miniform.withFormData(scheme)(props => {
  return (
    <form>
      <input type='checkbox' checked={props.consent.value} onChange={props.consent.onChange} /> Consent
    </form>
  );
});

export default InputForm;

Standalone radio button

:radio_button: Radio button

A standalone radio button uses a boolean scheme property value, which is passed on to the input element's checked property.

import withFormData from 'minimal-form-data-hoc';

const scheme = miniform.createFormScheme(() => ({
  consent: { value: false },
}));

const InputForm = miniform.withFormData(scheme)(props => {
  return (
    <form>
      <input type='radio' checked={props.consent.value} onChange={props.consent.onChange} /> Consent
    </form>
  );
});

export default InputForm;

Multiple checkboxes

:ballot_box_with_check: Coffee
:ballot_box_with_check: Tea
:white_large_square: Water

Multiple checkboxes are grouped by assigning the input elements the same name as the scheme property. The value of the scheme property is an array, while the value of the input elements are literals. These literals are added and removed from the array as the boxes are toggled.

import withFormData from 'minimal-form-data-hoc';

const scheme = miniform.createFormScheme(() => ({
  beverage: { value: [] },
}));

const InputForm = miniform.withFormData(scheme)(props => {
  return (
    <form>
      <input
        type='checkbox'
        name='beverage'
        value='coffee'
        checked={props.beverage.value.some((x: string) => x === 'coffee')}
        onChange={props.beverage.onChange}
      />
      Coffee
      <br />
      <input
        type='checkbox'
        name='beverage'
        value='tea'
        checked={props.beverage.value.some((x: string) => x === 'tea')}
        onChange={props.beverage.onChange}
      />
      Tea
      <br />
      <input
        type='checkbox'
        name='beverage'
        value='water'
        checked={props.beverage.value.some((x: string) => x === 'water')}
        onChange={props.beverage.onChange}
      />
      Water
    </form>
  );
});

export default InputForm;

Multiple radio buttons

:radio_button: Coffee
:radio_button: Tea
:radio_button: Water

Multiple radio buttons are grouped by assigning the input elements the same name as the scheme property. The values of the input elements are assigned to the form data state as the buttons are toggled.

import withFormData from 'minimal-form-data-hoc';

const scheme = miniform.createFormScheme(() => ({
  beverage: { value: '' },
}));

const InputForm = miniform.withFormData(scheme)(props => {
  return (
    <form>
      <input
        type='radio'
        name='beverage'
        value='coffee'
        checked={props.beverage.value === 'coffee'}
        onChange={props.beverage.onChange}
      />
      Coffee
      <br />
      <input
        type='radio'
        name='beverage'
        value='tea'
        checked={props.beverage.value === 'tea'}
        onChange={props.beverage.onChange}
      />
      Tea
      <br />
      <input
        type='radio'
        name='beverage'
        value='water'
        checked={props.beverage.value === 'water'}
        onChange={props.beverage.onChange}
      />
      Water
    </form>
  );
});

export default InputForm;

Troubleshooting

  • Warning: A component is changing an uncontrolled input to be controlled.

    Ensure that all initial values (defined in the scheme) are not null or undefined.

  • Type '(string | ((value: string) => boolean))[]' is not assignable to type 'readonly [any, (value: any) => boolean]'.

    Try defining the scheme as const to distinguish array and tuple types.

  • Argument of type '{ ... }' is not assignable to parameter of type '(props: {}) => Readonly<Record<string, Readonly<FormSchemeItem>>>'.

    Note that the withFormData HOC's first argument is a function and not an object. The function's input argument is the props of the wrapped component.

  • Property '...' does not exist on type 'PropsWithChildren<Readonly<Record<..., Readonly<FormDataItem>>>>'.

    Try typing the input argument of the scheme. This input type is merged with the type of the form data.