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

@jalik/react-form

v7.0.0

Published

Manage forms in React with ease.

Readme

@jalik/react-form

GitHub package.json version Build Status GitHub last commit GitHub issues GitHub npm @jalik/react-form

Why using this lib ?

There are other well established solutions like Formik, React-Hook-Form, Redux Form...
This lib aims to provide the best experience for developers (DX) and users (UX) when creating advanced forms in React (have a look at the features below).
If you feel concerned, then it's all for you :)

Features

  • Fields props initialization at form level (optional)
  • Management of field state and updates (value and onChange)
  • Tracking of modified fields
  • Tracking of touched fields
  • Various form status info (modified, disabled, validating, submitting...)
  • Form loading using promise (optional)
  • Auto-disabling fields until the form is initialized
  • Auto-disabling fields when the form is disabled, not modified, validating or submitting
  • Parsing of field value when modified (smart typing or custom parser)
  • Replacement of empty string by null on field change and form submit
  • Trim values on form submitting
  • Field validation on change (optional)
  • Field validation on init/load (optional)
  • Field validation on touch (optional)
  • Field validation on submitting (optional)
  • Field and form validation using a custom function or schema (like yup)
  • Form and field errors handling
  • Reset form or fields
  • Handling form submission errors and retries
  • Compatible with custom components libraries
  • TypeScript declarations ♥

Sandbox

You can play with the lib here: https://codesandbox.io/s/jalik-react-form-demo-wx6hg?file=/src/components/UserForm.js

Installing

npm i @jalik/react-form
yarn add @jalik/react-form

Creating a form

import { Button, Field, Form, useForm } from '@jalik/react-form'

/**
 * Authenticates by username and password.
 * @param username
 * @param password
 */
function authenticate (username, password) {
  return fetch('https://www.mysite.com/auth', {
    method: 'POST',
    body: JSON.stringify({
      username,
      password
    }),
    headers: { 'content-type': 'application/json' }
  })
}

function SignInForm () {
  const form = useForm({
    initialValues: {
      username: '',
      password: ''
    },
    // onSubmit needs to return a promise,
    // so the form is aware of the submit state.
    onSubmit: (values) => authenticate(values.username, values.password)
  })

  return (
    // Using the provided components allows writing code faster while keeping it very concise.
    // <Field> and other components must be nested in a <Form> with the form context. 
    <Form context={form}>
      <Field name="username" />
      <Field name="password" />
      <Button type="submit">Sign in</Button>
    </Form>
  )
}

Loading a form

There are several ways to load a form:

  • Loading values inside or outside the form component ;
  • Loading values using the load option of useForm() ;

Loading values inside the form component

import { Button, Field, Form, useForm } from '@jalik/react-form'
import { useEffect, useState } from 'react'
import { useParams } from 'react-router'

function UserFormPage () {
  const params = useParams()
  const [user, setUser] = useState(null)

  // Load user and call setUser(user)...

  const form = useForm({
    // initialValues must be null (or omitted) at first,
    // so the form will understand that it will be initialized later.
    initialValues: user,
    reinitialize: true,
    onSubmit: async (values) => ({ saved: true }),
  })

  return (
    <Form context={form}>
      <Field name="firstName" />
      <Field name="lastName" />
      <Button type="submit">Save</Button>
    </Form>
  )
}

Loading values using the load option in useForm()

import { Button, Field, Form, useForm } from '@jalik/react-form'
import { useCallback } from 'react'

function loadUser (id) {
  return fetch(`/api/user/${id}`).then((resp) => resp.json())
}

function UserFormPage (props) {
  const form = useForm({
    // initialValues must be null (or omitted) at first,
    // so the form will understand that it will be initialized later.
    initialValues: null,
    // WARNING: load is called every time it changes,
    // in this case the form will be updated when the id changes.
    // Note that all fields are disabled during loading.
    load: useCallback(() => loadUser(1337), []),
    onSubmit: async (values) => ({ saved: true }),
  })

  return (
    <Form context={form}>
      <Field name="firstName" />
      <Field name="lastName" />
      <Button type="submit">Save</Button>
    </Form>
  )
}

Validating a form

Validating using a schema

Form validation using a schema needs a small amount of work.
Here we use @jalik/schema to validate the form using a schema, but it is possible to use any lib (yup, joi...).

import { Button, Field, FieldError, Form, useForm } from '@jalik/react-form'
import Schema from '@jalik/schema'

/**
 * Returns field props based on schema constraints.
 * @param schema
 */
export function createFieldInitializer (schema) {
  // function called by initializeField
  return (name) => {
    const field = schema.getField(name)
    return field ? {
      required: field.isRequired()
    } : null
  }
}

/**
 * Validates the field using the schema.
 * @param schema
 */
export function createFieldValidator (schema) {
  // function called by validateField
  return async (name, value) => {
    schema.getField(name).validate(value)
  }
}

/**
 * The function returned validates the form (all fields) using the schema.
 * @param schema
 */
export function createFormValidator (schema) {
  // function called by validate
  return async (values) => schema.getErrors(values)
}

// Prepare the form validation schema.
const SignInFormSchema = new Schema({
  username: {
    type: 'string',
    required: true,
    minLength: 1
  },
  password: {
    type: 'string',
    required: true,
    minLength: 1
  }
})

function SignInForm () {
  const form = useForm({
    initialValues: {
      username: null,
      password: null
    },
    // This function sets the fields props based on a schema.
    initializeField: createFieldInitializer(SignInFormSchema),
    // This function validates all fields (even missing ones) based on a schema.
    validate: createFormValidator(SignInFormSchema),
    // This function validates a single field based on a schema.
    validateField: createFieldValidator(SignInFormSchema),
    onSubmit: async (values) => ({ success: true })
  })
  return (
    <Form context={form}>
      <Field name="username" />
      <FieldError name="username" />

      <Field name="password" />
      <FieldError name="password" />

      <Button type="submit">Sign in</Button>
    </Form>
  )
}

Form component linking

To link and initialize your components with a form instance, use getButtonProps(), getFieldProps() and getFormProps().

import { useFormContext } from '@jalik/react-form'
import { MyCustomButton, MyCustomForm, MyCustomInput } from 'my-custom-ui'

export function FormButton (props) {
  const form = useFormContext()
  return <MyCustomButton {...form.getButtonProps(props)} />
}

export function FormInput (props) {
  const { name } = props
  const form = useFormContext()
  return <MyCustomInput {...form.getFieldProps(name, props)} />
}

export function FormWrapper (props) {
  const form = useFormContext()
  return <MyCustomForm {...form.getFormProps(props)} />
}

Or use the provided components <Button>, <Field> and <Form>.

import { Button, Field, Form } from '@jalik/react-form'
import { MyCustomButton, MyCustomForm, MyCustomInput } from 'my-custom-ui'

export function FormButton (props) {
  return <Button {...props} component={MyCustomButton} />
}

export function FormInput (props) {
  return <Field {...props} component={MyCustomInput} />
}

export function FormWrapper (props) {
  return <Form {...props} component={MyCustomForm} />
}

API

Hooks

useForm (options)

This is where the magic happens, this hook defines the form state and its behavior.

import { useForm } from '@jalik/react-form'

const form = useForm({
  // optional, what to do after a successful submit: 'clear' | 'initialize' | 'reset' | null
  afterSubmit: 'reset',
  // optional, used to debug form
  debug: false,
  // optional, used to disable all fields and buttons
  disabled: false,
  disableOnSubmit: true,
  disableOnValidate: true,
  // optional, disables the submit button if form is not modified
  disableSubmitIfNotModified: false,
  // optional, disables the submit button if form is not valid
  disableSubmitIfNotValid: false,
  // optional, enable native HTML validation on submit
  enableHTMLValidation: false,
  // optional, update form when status changes
  forceUpdateOnStatusChange: false,
  // optional, used to set initial values
  initialValues: undefined,
  // optional, sets initial errors
  initialErrors: {},
  // optional, sets initial modified fields
  initialModified: {},
  // optional, sets initial touched fields
  initialTouched: {},
  // optional, used to replace empty string by null on change and on submit
  nullify: false,
  // optional, prevent native action on submit
  preventDefaultOnSubmit: true,
  // optional, used to initialize form everytime initialValues changes
  reinitialize: false,
  // optional, used to debounce submit
  submitDelay: 100,
  // optional, used to remove extra spaces on blur
  trimOnBlur: false,
  // optional, used to remove extra spaces on submit
  trimOnSubmit: false,
  // optional, used to debounce validation
  validateDelay: 400,
  // optional, used to validate field on change
  validateOnChange: false,
  // optional, used to validate all fields on initialization
  validateOnInit: false,
  // optional, used to validate all fields on submit
  validateOnSubmit: true,
  // optional, used to validate field on touch
  validateOnTouch: false,

  // optional, used to set field props dynamically
  initializeField: (name, formState) => ({
    className: formState.modifiedFields[name] ? 'input-modified' : undefined,
    required: name === 'username'
  }),

  // optional, used to load initial values
  load: async () => ({
    id: 1,
    username: 'test'
  }),

  // REQUIRED, called when form is submitted
  onSubmit: async (values) => ({ success: true }),

  // optional, called when form has been successfully submitted
  onSuccess: (result, values) => {
    // resuls contains the value returned by onSubmit, in this example { success: true }
  },

  // optional, called when form values have changed
  onValuesChange: (values, previousValues) => {
  },

  // optional, called when a field value changed
  // mutation contains all pending changes in a flat object ({field: value})
  // values contains the next form values
  transform: (mutation, values) => {
    // in this example, if lastname or firstname changed,
    // we set the value of "username" like "john.c"
    if (mutation.lastname || mutation.firstname) {
      mutation.username = [
        values.firstname,
        (values.lastname || '')[0]
      ].join('.').toLowerCase()
    }
    return mutation
  },

  // optional, used to validate all fields (expect a promise)
  validate: async (values) => {
    const errors = {}

    if (!values.username) {
      // error can be a string
      errors.username = 'field is required'
      // or an Error
      errors.username = new Error('field is required')
    }
    return errors
  },

  // optional, used to validate a single field (expect a promise)
  validateField: async (name, value, values) => {
    if (name === 'username' && !value) {
      // error can be a string
      return 'field is required'
      // or an Error
      return new Error('field is required')
    }
  }
})

useFormContext ()

This hook returns the form context and functions.

import { useFormContext } from '@jalik/react-form'

const {
  // FORM STATE
  // tells if the form is disabled
  disabled,
  // tells if the form has been initialized
  initialized,
  // tells if the form was modified
  modified,
  // tells if the form was touched
  touched,

  // FORM UTILS
  // returns the button props
  getButtonProps,
  // returns the field props
  getFieldProps,
  // handler for onChange
  handleChange,
  // handler for onBlur
  handleBlur,
  // handler for onReset
  handleReset,
  // handler for onChange((value) => {}) instead of onChange((event) => {})
  handleSetValue,
  // handler for onSubmit
  handleSubmit,

  // LOADING
  load,
  // tells if the form is loading
  loading,
  // loading error (if any)
  loadError,

  // FIELD VALUES
  // clears the form (values, errors...)
  clearValues,
  // returns the field initial value by name
  getInitialValue,
  // returns the field initial value by name
  getValue,
  // initial values (used when form is reset)
  initialValues,
  // removes fields (used for dynamic forms)
  removeValues,
  // resets all or given fields to their initial values
  resetValues,
  // sets the initial values
  setInitialValues,
  // sets value of a field
  setValue,
  // sets values of multiple fields
  setValues,
  // the form values
  values,

  // LIST MANAGEMENT
  appendListItem,
  insertListItem,
  moveListItem,
  prependListItem,
  removeListItem,
  replaceListItem,
  swapListItem,

  // FIELD ERRORS
  // clears all errors
  clearErrors,
  // fields errors
  errors,
  // tells if the form has errors
  hasError,
  // sets a single field error
  setError,
  // sets fields errors
  setErrors,

  // FIELD STATE MANAGEMENT
  // clears all or given fields
  clearModifiedFields,
  // clears all or given fields
  clearTouchedFields,
  // returns the modified fields
  getModifiedFields,
  // returns the touched fields
  getTouchedFields,
  // check if a field was modified
  isModified,
  // check if a field was touched
  isTouched,
  // the list of modified fields
  modifiedFields,
  // resets modified fields to their initial values
  resetModifiedFields,
  // resets touched fields to their initial values
  resetTouchedFields,
  // set a single modified field
  setModifiedField,
  // set all or given modified field
  setModifiedFields,
  // set a single touched field
  setTouchedField,
  // set all or given touched field
  setTouchedFields,
  // the list of touched fields
  touchedFields,

  // FORM SUBMISSION
  // submits the form with values (validate first)
  submit,
  // the number of times the form was submitted.
  // resets to zero when submission succeeds.
  submitCount,
  // the submit error (if any)
  submitError,
  // the submit result (returned by onSubmit)
  submitResult,
  // tells if the form was submitted (changes to false when form is modified)
  submitted,
  // tells if the form is submitting
  submitting,

  // VALIDATION
  // tells if the form will trigger a validation
  // can be a boolean or a list of fields to validate
  needValidation,
  // sets the validation state of the form
  setValidated,
  // sets the validation error
  setValidateError,
  // tells if the form is validating
  setValidating,
  // validates all fields
  validate,
  // the validation error (if any)
  // happens only when an error is thrown during validation
  // it's different from the field validation errors
  validateError,
  // validates given fields
  validateFields,
  // tells if the form was successfully validated
  validated,
  // tells if a field should be validated on change
  validateOnChange,
  // tells if all fields should be validated on initialization
  validateOnInit,
  // tells if all fields should be validated on submit
  validateOnSubmit,
  // tells if a field should be validated on touch
  validateOnTouch,
  // tells if the form is validating
  validating,

  // WATCH
  watch,
  watchers,
} = useFormContext()

useFieldArray (options)

This hook returns utils to manage an array of fields.

import { useFieldArray, useForm } from '@jalik/react-form'

function ItemListForm () {
  const form = useForm({
    initialValues: {
      items: [{ name: 'Item 1' }]
    },
    onSubmit: (values) => Promise.resolve(values)
  })

  const {
    items,
    append,
    prepend,
    remove,
    insert,
    replace,
    move,
    swap,
    handleAppend,
    handlePrepend,
    handleRemove
  } = useFieldArray({
    context: form,
    name: 'items',
    defaultValue: { name: 'New Item' }
  })

  return (
    <div>
      {items.map((item, index) => (
        <div key={item.key}>
          <Field name={item.name} />
          <button type="button" onClick={handleRemove(index)}>Remove</button>
        </div>
      ))}
      <button type="button" onClick={handleAppend}>Add Item</button>
    </div>
  )
}

Components

Some components are provided to ease forms building.

<Button>

This component is synced with the form, so whenever the form is disabled (because it is loading, validating or submitting), the button disabled.

import { Button } from '@jalik/react-form'

function SubmitButton () {
  return (
    <Button type="submit">Submit</Button>
  )
}

<Field>

This component handles the field value and logic.
The name is required.

import { Field } from '@jalik/react-form'
import { Switch } from '@mantine/core'

function parseBoolean (value) {
  return /^true|1$/gi.test(value)
}

export function AcceptTermsField () {
  return (
    <Field
      component={Switch}
      name="acceptTerms"
      parser={parseBoolean}
      type="checkbox"
      value="true"
    />
  )
}

export function CountryField () {
  return (
    <Field
      name="country"
      type="select"
      options={[
        {
          label: 'French Polynesia',
          value: 'pf'
        },
        {
          label: 'New Zealand',
          value: 'nz'
        }
      ]}
    />
  )
}

<FieldError>

This component automatically displays the field error (if any).

import { Field, FieldError } from '@jalik/react-form'

export function PasswordField () {
  return (
    <>
      <Field
        name="password"
        type="password"
      />
      <FieldError name="password" />
    </>
  )
}

<Form>

This component contains the form context, so any component nested in a Form can access the form context using useFormContext().

import { Button, Field, Form, useForm } from '@jalik/react-form'

export function SignInForm () {
  const form = useForm({
    onSubmit: (values) => Promise.resolve(true)
  })
  return (
    <Form context={form}>
      <Field name="username" />
      <Field name="password" />
      <Button type="submit">Sign in</Button>
    </Form>
  )
} 

Changelog

History of releases is in the changelog.

License

The code is released under the MIT License.