@stackup/form

A type-safe approach to managing complex form state in React.

Usage no npm install needed!

<script type="module">
  import stackupForm from 'https://cdn.skypack.dev/@stackup/form';
</script>

README

@stackup/form

Build Version Size License

A type-safe approach to managing complex form state in React.

This library provides integration with @stackup/validate to handle validation. However, it should be pretty easy to integrate whatever validation library you prefer.

You should also checkout @stackup/inputs, which includes some ready-made form fields.

Usage

import React from "react";
import { useForm, useField } from "@stackup/form";

const Form = () => {
  const form = useForm({ initialValue: { email: "", name: "" } });

  const validate = useValidation(form, value => {
    if (value.email) {
      return { valid: true, value };
    } else {
      return { valid: false, error: { email: "is required" } };
    }
  });

  const submit = useSubmit(validate, value => {
    console.log(value);
  });

  return (
    <form onSubmit={submit}>
      <Input type="email" label="Email" field={useField(form, "email")} />
      <Input type="text" label="Name" field={useField(form, "name")} />
      <button type="submit">Save</button>
    </form>
  );
};

const Input = ({
  label,
  field: { id, value, error, touched, setValue, setTouched },
  ...props
}) => (
  <div>
    <label htmlFor={id}>{label}</label>
    <input
      {...props}
      id={id}
      value={value}
      onBlur={useCallback(() => setTouched(true), [])}
      onChange={useCallback(e => setValue(e.target.value), [setValue])}
    />
    {touched && error && <p>{error}</p>}
  </div>
);

API

Table of Contents

useForm

Create a new form. A form requires an initial value, a function to validate, and a submit handler.

A form behaves just like any other field, but with some extra properties for managing submission.

The initial value for the form can be literally anything! Usually, it's an object, but it could be any type of value.

Parameters

Examples

Form values can be primitive

const form = useForm({ initialValue: "" });

But usually, they'll contain an object

const form = useForm({
  initialValue: {
    email: "",
    name: ""
  }
});

Returns Form<Value>

useField

Create a field for a given property.

Parameters

Examples

const form = useForm({
  initialValue: {
    email: "",
    profile: { name: "" } }
  }
});

const email = useField(form, "email");
const profile = useField(form, "profile");
const name = useField(profile, "name");

Returns FormField<any>

useValidation

Use a plain ol' function for validation.

This hook can also be used to incorporate your favorite validation library.

Parameters

Examples

const validation = useValidation(form, value => {
  if (!value.email) {
    return { valid: false, error: { email: "can't be blank" } };
  }

  return { valid: true, value };
});

Returns Form<Value, Result>

useValidate

Add validation to the form using @stackup/validate.

Parameters

Examples

const validator = schema({
  email: assert(isString).then(refute(isBlank))
});

const validate = useValidate(form, validator);

Returns Form<Value, Result>

useSubmit

Create a submit handler for the form.

Parameters

  • form Form<Value, Result>
  • fn SubmitFn<Result>

Examples

Sumbitting a form

const form = useForm({ initialValue: "foo" });
const submit = useSubmit(form, console.log);

Sumbitting with validation

const form = useForm({ initialValue: "foo" });
const validate = useValidate(form, myValidator);
const submit = useSubmit(validate, console.log);

Returns Submit

useReset

Create a reset handler for the form.

Parameters

  • form Form<Value, Result>

Examples

const form = useForm({ initialValue: "foo" });
const reset = useReset(form);

Returns Reset

useFieldItem

Create a field for a specific index in an array.

This hook is intended for use in building forms with "Add another" functionality.

Parameters

Examples

const form = useForm({ initialValue: { pets: [{ name: "" }] } });
const pets = useField(form, "pets");
const pet = useFieldItem(pets, 0);
const name = useField(pet, "name");

Returns FormField<Value>

usePushItem

Adds a new value to the end to an array of values.

This can be used to create a form with repeating fields.

Parameters

Examples

const pets = useField(form, "pets");
const pet = useFieldItem(pets, 0);
const addPet = usePushItem(pets, { name: "" });

useInsertItem

Adds a new value at a specific position to an array of values.

This can be used to create a form with repeating fields.

Parameters

Examples

const pets = useField(form, "pets");
const pet = useFieldItem(pets, 0);
const insert = useInsertItem(pets, 0, { name: "" });

useRemoveItem

Removes a value at the given index from array of values.

This can be used to create a form with repeating fields.

Parameters

Examples

const pets = useField(form, "pets");
const pet = useFieldItem(pets, 0);
const removePet = useRemoveItem(pets, 0);

useIdentifier

Creates a unique identifier that will remain consistent across re-renders.

This hook does not currently support SSR.

Parameters

Returns string

Form

Extends FormField<Value>

The value returned by useForm.

initialValue

The initial values for the form.

Type: Value

initialError

The initial errors on the fields.

Type: FormError<Value>

initialTouched

The initially touched fields.

Type: FormTouched<Value>

setValidating

Indicate that the form is validating

Type: SetState<boolean>

setSubmitting

Indicate that the form is validating

Type: SetState<boolean>

setSubmission

Update the form's submission status

Type: SetState<Submission>

reset

Reset the state of the form

Type: function (opts: ResetOptions<Value>): void

validate

Run validation

Type: function (opts: ValidateOptions): Promise<ValidationResult<Value, Result>>

FormField

The primary form data structure.

id

A unique ID for this form field. This can be used to associate fields with a label.

Type: string

name

The name or array index that was given to useField or useFieldItem.

Type: (string | number)

value

The current value of the field.

Type: Value

error

An error or errors that are associated with this field or it's children.

Type: FormError<Value>

touched

Indicates that this field or it's children have been modified by the user.

Type: FormTouched<Value>

isValidating

Indicates that validation is currently being run

Type: boolean

isSubmitting

Indicates that the form is currently being submitted

Type: boolean

submission

Keeps track of the form's submission status

Type: Submission

setValue

Change the value. Just like with setState, you can pass a callback to this function to get the current value and update it.

Type: SetState<Value>

setError

Update the error.

Type: SetState<FormError<Value>>

setTouched

Indicate that this field has been touched. This is usually called in onBlur.

Type: SetState<FormTouched<Value>>

ValidateFn

A function used for validation. This function must indicate whether or not the form is valid.

The error property can be used to set errors on the form.

The value property can be used to transform the form's values before validation.

Type: function (value: Value): (ValidationResult<Value, Result> | PromiseLike<ValidationResult<Value, Result>>)

Submit

Submits the form.

Type: function (event: FormEvent<HTMLFormElement>): Promise<void>

Submission

Keeps track of submissions.

count

The number of times the form has been submitted

Type: number

error

If the submission flow throws an error, it will appear here.

Type: Error

Reset

Resets the form.

Type: function (event: FormEvent<HTMLFormElement>): Promise<void>

UseFormOptions

The options that can be passed to useForm.

id

Customize the base ID for all fields.

Type: string

initialValue

The initial values for the form.

Type: Value

initialError

The initial errors on the fields.

Type: FormError<Value>

initialTouched

The initially touched fields.

Type: FormTouched<Value>

UseValidationOptions

Configures when validation runs.

onChange

Enables validation whenever values change.

Type: boolean

onBlur

Enables validation whenever a field is touched.

Type: boolean