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

hoth-form

v0.1.0

Published

Another form builder for React

Downloads

24

Readme

hoth-form

Another way to work with forms in React. See demo.

Install it

npm i -S hoth-form

Test it

git clone https://github.com/saintcrawler/hoth-form && cd hoth-form

npm i && npm test

Use it

Import or require hoth-form module. Currently, it exposes these items:

  1. Form - React component, which will render your form.
  2. Field - React component, which will render your form fields. It is not the only way to render form fields, but you can use it as default.
  3. config - Configuration object, which you may tune as you wish.

Make a config object:

const fields = {
  username: '',                   // init with default value
  password: {                     // or object
    value: '123',                 // or `initValue`, or nothing (empty string will be used)
    className: 'password-field',  // almost everything will be injected on render
    validation: {                 // except for some non-standard props... 
      required: true              // they will not get to the HTML form controls (input, select, etc.)...
    }                             // only to the custom components (Field, etc.)
  },
  transport: {
    // for multi-values fields you must specify `fields` prop with an array of `value` names or with object with `value` named keys. 
    fields: ['feet', 'car', 'bicycle'],
    // value should be a string matching one of the `fields` names (for radio-buttons or selects with single selection mode) or an array of strings (for checkboxes or selects with multiple selection) 
    value: ['feet', 'bicycle']
  },
  cars: {
    fields: {
      ford: {}, 
      mazda: {},
      audi: {disabled: true}, 
    },
    value: []
  }, 
  agree: false // single checkbox or radio should be init with true/false value
};

Use <Form /> component instead of html <form /> tag. And any child component with name prop, matching against fields keys, will be injected with corresponding props. For example:

import {Form, Field} from 'hoth-form'

// optional
function onChange(fields, target) {
  // is called AFTER changing a value, so `fields` are up-to-date
  // `target` is React component, that was changed
  // return either object with keys matching form fields or empty object or undefined
  return {
    password: {disabled: !!fields.username.value}
  }
}

// optional
function validate(fields) {  
  // return either object with keys matching form fields 
  // plus special non-field-errors field (defaults to 'nonFieldErrors', 
  // can be changed in the config object by setting 'nonFieldErrorsKey')
  // or empty object or undefined
  // Each object key must be a string or an array of strings
  const errors = {};
  if (!fields.agree.value) errors.agree = 'You must accept something';
  if (fields.username.value.length < 3) errors.username = [
    'You must enter a valid username',
    'Username must be at least 3 characters long'
  ];
  return errors;
}

// required
function onSubmit(form) {
  console.log('submitting form', form);
}

function isSubmitEnabled(form) {
  return {disabled: form.errors}
}

const moreErrors = {
  // This will be injected into fields with corresponding `name` prop.
  // You can use this, for example, to provide server validation errors from another redux state slice.
  // These errors do not affect form `errors` flag.
  username: 'Already taken'
};

//...somewhere in render function...
<Form id="profile"            // optional
      fields={fields}         // required
      onSubmit={onSubmit}     // required
      onChange={onChange}     // optional
      validate={validate}     // optional
      moreErrors={moreErrors} // optional
      >
  // use <Field/> to conveniently display label and field errors
  <Field name="username" placeholder="Username" label="Name" />
  // or use html form tags
  <input name="password" type="password" />
  <fieldset className="group">
    <legend>Transport</legend>
    <Field type="checkbox" name="transport" value="feet" label="Feet" />
    <Field type="checkbox" name="transport" value="car" label="Car" />
    <Field type="checkbox" name="transport" value="bicycle" label="Bicycle" />
    <Field widget="select" multiple name="cars" label="Cars">
      <option value="ford">Ford</option>
      <option value="mazda">Mazda</option>
      <option value="audi">Audi</option>   
    </Field>
  </fieldset>
  <Field type="checkbox" name="agree" label="Agree" />
  <Field widget={null} name="nonFieldErrors" />
  <button hoth={isSubmitEnabled}>Submit</button>
</Form>

Structure of a field

For single values:

  • initialValue
  • value
  • dirty (true/false)
  • active (true/false)
  • errors ([] or null)
  • checked (true/false) - for radio and checkboxes

For multiple values:

  • fields
    • subField1
      • checked (true/false)
    • subField2
    • subField3
  • initialValue ([])
  • value ([])
  • dirty
  • active
  • errors

Single vs Multiple value

Single can be: text-like inputs, <textarea>, checkbox with true/false logic.

Multiple are: radio inputs, checkboxes with multi-selection logic, <select> elements.

Structure of a form state

  • reset - call this function to reset form fields (see example in demo)
  • errors (true/false) - auto-calculation based only on errors prop of each field
  • fields
    • myField1
    • myField2
    • nonFieldErrors

Get form state

In a component inside your form you can obtain current form state by specifying a function under the hoth prop. On render this function will be called and its result will be injected into a component as props.

Accessing deep-level components

On render, Form looks only for its direct children. If your form has a complex html layout you can specify a class for a parent container, that will tell Form to also look inside this container's children. That behavior is recursive. By default, value of a class is group.

Config object

import {config} from 'hoth-form'

config.nonFieldErrorsKey = 'whatever';   // but should not collide with other fields
config.fieldGroupClassName = /myGroup/;  // or any other regex

Field component

Supports label prop. Renders errors and moreErrors together as a list. Sets dirty/pristine and valid/invalid classes.

Custom props that will be filtered out

['initialValue', 'active', 'dirty', 'errors', 'moreErrors', 'fields', 'validation', 'widget', 'label', 'hoth', 'extra' ]

License

ISC