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

ark-forms

v2.0.4

Published

<p align="center"> <img src="https://dmytroyeremieiev.github.io/ark-form/images/logo1.svg" height="220"> <h1 align="center">Ark Form validation library</h1> </p>

Readme

Table of Contents

Overview

  • small, ultra fast and flexible react based form validation library;
  • predictable and synchronous validation flow, clear and fast test suits
  • allows granularly fine-tune each field validation trigger. E.g., consider you need the 1-st field to be validated after onChange event occurred and the second field only after onBlur event;
  • no external dependencies;
  • fully written in typescript;
  • 2.6 kb minified & gzipped;
  • compatible with React v16.8+;

Codesandbox demos

Installation

npm install ark-form --save or yarn add ark-form

Motivation

Why not formik?

  • extra re-renders, e.g., one field value changes, all other fields within same form undergo re-render;
  • can't granularly fine-tune each field validation trigger. All fields within the form are subject to the same validation trigger's rules(validateOnBlur, validateOnChange exposed only on a top form level);
  • formik asynchronous validation nature requires the use of await constructs: example1, example2, example3, example4.
  • bigger lib size: > ~12kb minified & gzipped
  • no dirty/pristine indicators' native support for a particular field(you need to resort to custom state fieldMeta.touched && fieldMeta.initialValue !== fieldMeta.value constructs);

Collaboration

Library source files are located at ./ark-forms/src.

Tests reside at ./ark-forms/__tests__ and ./web/__tests__.

web - next.js and web-cra - cra projects are sandboxes of real-world use.

Top-level architecture

ark-from library is based on several components:

General

The general data flow

All data flow except form submitting) flows start at <ArkField/> components which listen for proper event type.

  1. change or blur event happens to the input wrapped in a field component <ArkField/>;
  2. Calculating new field state with fieldReducer;
  3. Dispatching new field state to formReducer, triggering entire form state re-evaluation;
  4. Propagating new form & field states using FormContext downwards;

Field state evaluation logic

when a change event occurs:

General

when a blur event occurs:

General

Validation

All validation depends on auxiliary function validate which executed within Calculate field validity stage(field state evaluation logic).

    interface BasicInput<ET> {
      // ...
      validate?: (value?: string) => ValidityStateInterface;
      // ...
    }
    interface ValidityStateInterface extends Record<string, any>  {
        valid: boolean;
        className?: string;
        errorMessage?: string;
    }

<ArkForm/> component

  • holds inner <form/> element & <Field> components;
  • manages form state, configuration, creates <FormContext/>
  • distributes it through <FormContext/> between inner <ArkField> components.

Hooking-up managed state with <form/> elem happens through setting-up name, onSubmit, onChange, onBlur props on your elem. However there's shortcut, through spread operator {...formProps}:

    <ArkForm>
      {({ state, formProps }) => (
        <form name={name} {...formProps}>
          {children}
        </form>
      )}
    </ArkForm>

<ArkForm/> props:

| Props | Description | Default Value | | :--------------- | :----------------------------------------------------------- | ------------: | | name | <form/> name | none | | onSubmit | onsubmit event handler | none | | onChange | onchange event handler, called on any inner field change | none | | validateOnBlur | Runs fields validation on blur | true | | validateOnChange | Runs fields validation on change | false |

<ArkField/> component

  • encapsulates input field state
  • uses children render prop technique in order to share managed state with user's components
  • implicitly connected to parent form state through FormContext

Hooking-up managed state with html input elem happens through setting-up value, ref, onChange, onBlur, onFocus props on your input elem:

<ArkField>
  {({ fieldProps, fieldState, formContext }) => (
      <div>
          <input id='field1' type='text' {...fieldProps} />
          <label htmlFor='field1'>Field 1</label>
      </div>
  )}
</ArkField>

<ArkField/> props:

| Prop | Description | Default | | ------------ | --------------------------- | ------- | | name | Field name | none | | initialValue | Field initial value | none | | onChange | onchange event handler | none | | onFocus | onfocus event handler | none | | onBlur | onblur event handler | none | | validate | your own validator callback | none |

How manually set the field state

First, you need to hook up to a form context:

export interface FormContextInterface {
  state: FormState;
  dispatch: React.Dispatch<FormAction>;
  setFieldState: (name: string, setState: (currState: FieldState) => DeepPartial<FieldState>) => void;
  setFieldValue: (name: string, value: string, configuration?: Partial<FieldConfiguration>) => void;
}

Within <ArkForm/>, you can call for the form context:

  const formContext = useFormContext();

Outside of <ArkForm/>, pass ref obj:

  ...
  const contextRef = useRef();
  return <ArkForm formContextRef={contextRef}>
    {({ formContext, formProps }) => (
      <form name={name} {...formProps}>
        {children}
      </form>
    )}
  </ArkForm>

Once you get formContext reference, you're free to use formContext.dispatch, method to alter the form state in any imaginative way. Internally, all components operate only through dispatch method and formReducer, fieldReducer reducers.

Here's implementations of setFieldState, setFieldValue helper methods exposed publicly to cover most of user's needs:

  const setFieldState: FormContextInterface['setFieldState'] = (name, setNewState) => {
    const newState = setNewState(getFieldState(name));
    const mergedNewState = mergeState(getFieldState(name), newState);
    const validatedState = fieldReducer(mergedNewState, { type: 'validate' });
    dispatch({
      type: 'setField',
      fieldState: validatedState,
    });
  };

  const setFieldValue = (name: string, value: string, configuration?: Partial<FieldConfiguration>) => {
    const state = getFieldState(name);
    const newFieldState = fieldReducer(state, {
      value: value,
      type: 'change',
      configuration: { ...state.configuration, ...configuration, validateOnChange: true },
    });
    dispatch({
      type: 'change',
      fieldState: newFieldState,
    });
  };

You can peek more setFieldState, setFieldValue usages examples at /web/components/TestSuit.tsx.

Setting field valid:

  formContext.setFieldState(name, () => ({
    configuration: {
      validate: value => ({valid: true}),
    },
  }))

Setting field dirty:

  formContext.setFieldState(name, () => ({ dirty: true, pristine: false }))

Setting field pristine:

  formContext.setFieldState(name, () => ({ dirty: false, pristine: true }))

Consider you having some custom and complex validation logic described at:

  const checkValidity = (
    value?: string,
    pattern?: {
      regexp: RegExp;
      message?: string;
    },
    required?: boolean
  ): ValidityStateInterface => {
    const result: ValidityStateInterface = {
      valid: true,
    };
    if (required && !value) {
      result.className = FieldStateClassNames.requiredError;
      result.valid = false;
      return result;
    }
    if (pattern && value && !pattern.regexp.test(value)) {
      result.className = FieldStateClassNames.patternError;
      result.valid = false;
      result.errorMessage = pattern.message || 'Invalid value';
      return result;
    }
    return result;
  };
  export const TextInput = ({ initialValue = '', name, label, pattern, required, readOnly, ...rest }) => {
    return (
      <ArkField
        name={name}
        validate={value => checkValidity(value, pattern, required)}
        initialValue={initialValue}
        {...rest}
      >
        {({ fieldProps, fieldState, formContext }) => {
          const id = (formContext.state.configuration.name || '') + '-' + name;

          let ErrorMessage = null;
          if (
            fieldState.validity.errorMessage &&
            !fieldState.validity.valid &&
            (fieldState.dirty || formContext.state.submitted)
          ) {
            ErrorMessage = <span className='error'>{fieldState.validity.errorMessage}</span>;
          }

          return (
            <div>
              <div
                title={`${name} field`}
                className={`txo-input-container ${classnames(
                  {
                    [FieldStateClassNames.filled]: fieldState.filled,
                    [FieldStateClassNames.pristine]: fieldState.pristine,
                    [FieldStateClassNames.dirty]: fieldState.dirty,
                    [FieldStateClassNames.invalid]: !fieldState.validity.valid,
                    [FieldStateClassNames.valid]: fieldState.validity.valid,
                  },
                  {
                    [fieldState.validity.className]: fieldState.validity.className && !fieldState.validity.valid,
                  }
                )}`}
              >
                <input id={id} type='text' readOnly={readOnly} {...fieldProps} />
                <label htmlFor={id}>{label}</label>
              </div>
              {ErrorMessage}
            </div>
          );
        }}
      </ArkField>
    );
  };

, then in order to maintain all existing validation rules except mandatory requirement rule you will just need to update your custom validator checkValidity arguments:

  formContext.setFieldState(name, () => ({
    configuration: {
      validate: value => checkValidity(value, pattern, false),
    },
  }))

Resetting field state:

  formContext.setFieldState(name, () => ({
    ...defaultFieldState,
    configuration: {
      validate: value => checkValidity(value, pattern, required),
    },
  }))

Setting field value:

  formContext.setFieldValue(name, 'Some new value')

Connecting to more complex elements

Plain and simple examples on how to create and connect with a form validation more complex input elements. Original source code is under ./web/components/**.