An open API service indexing awesome lists of open source software.

https://github.com/tkrotoff/react-form-with-constraints

Simple form validation for React
https://github.com/tkrotoff/react-form-with-constraints

constraints form form-validation html5 input-validation react validation

Last synced: 8 months ago
JSON representation

Simple form validation for React

Awesome Lists containing this project

README

          

# react-form-with-constraints

[![npm version](https://badge.fury.io/js/react-form-with-constraints.svg)](https://badge.fury.io/js/react-form-with-constraints)
[![Node.js CI](https://github.com/tkrotoff/react-form-with-constraints/workflows/Node.js%20CI/badge.svg?branch=master)](https://github.com/tkrotoff/react-form-with-constraints/actions)
[![codecov](https://codecov.io/gh/tkrotoff/react-form-with-constraints/branch/master/graph/badge.svg)](https://codecov.io/gh/tkrotoff/react-form-with-constraints)
[![Bundle size](https://badgen.net/bundlephobia/minzip/react-form-with-constraints)](https://bundlephobia.com/result?p=react-form-with-constraints@latest)
[![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![Airbnb Code Style](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript)

Simple form validation for React

- Installation: `npm install react-form-with-constraints`
- CDN: https://unpkg.com/react-form-with-constraints/dist/

Check the [changelog](CHANGELOG.md) for breaking changes and fixes between releases.

## Introduction: what is HTML5 form validation?

⚠️ [Client side validation is cosmetic, you should not rely on it to enforce security](https://stackoverflow.com/q/162159)

```HTML

Email:

Submit

```

![input required](doc/input-required.png)
![input type="email"](doc/input-type-email.png)

The `required` HTML5 attribute specifies that the user must fill in a value, [`type="email"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email) checks that the entered text looks like an email address.

Resources:

- [Making Forms Fabulous with HTML5](https://web.archive.org/web/20220317195146/https://www.html5rocks.com/en/tutorials/forms/html5forms/)
- [Constraint Validation: Native Client Side Validation for Web Forms](https://web.archive.org/web/20210818094708/https://www.html5rocks.com/en/tutorials/forms/constraintvalidation/)
- [web.dev - Learn Forms](https://web.dev/learn/forms/)
- [MDN - Form data validation](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation)
- [MDN - Form input types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form__types)
- [UX Research Articles - Usability Testing of Inline Form Validation](https://baymard.com/blog/inline-form-validation)

## What react-form-with-constraints brings

- Minimal API and footprint
- Unobtrusive: easy to adapt regular [React code](https://reactjs.org/docs/forms.html)
- HTML5 error messages personalization: `My custom error message`
- Custom constraints: ` ...}>`
- Warnings and infos: ``, ``
- Async validation
- No dependency beside React (no Redux, MobX...)
- Re-render only what's necessary
- Easily extendable
- [Bootstrap](examples/Bootstrap) styling with npm package `react-form-with-constraints-bootstrap`
- [Material-UI](examples/MaterialUI) integration with npm package `react-form-with-constraints-material-ui`
- Support for [React Native](examples/ReactNative) with npm package `react-form-with-constraints-native`
- ...

```JSX



Should be at least 5 characters long

!/\d/.test(value)} warning>
Should contain numbers

!/[a-z]/.test(value)} warning>
Should contain small letters

!/[A-Z]/.test(value)} warning>
Should contain capital letters

```

## Examples

- CodePen basic Password example: https://codepen.io/tkrotoff/pen/BRGdqL ([StackBlitz version](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/Password))

![example-password](doc/example-password.png)

- [Bootstrap example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/Bootstrap)
- [Material-UI example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/MaterialUI)
- [WizardForm example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/WizardForm)
- [SignUp example (React classes)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/SignUp)
- [ClubMembers example (React classes + MobX)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/ClubMembers)
- [Password without state example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/PasswordWithoutState)
- [Server-side rendering example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/ServerSideRendering)

- [React Native example (React classes)](examples/ReactNative):

| iOS | Android |
| ----------------------------------------------------- | ------------------------------------------------------------- |
| ![react-native-example-ios](doc/react-native-ios.png) | ![react-native-example-android](doc/react-native-android.png) |

- Other examples from [the examples directory](examples):
- [Plain old React form validation example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/PlainOldReact)
- [React with HTML5 constraint validation API example (React hooks)](https://stackblitz.com/github/tkrotoff/react-form-with-constraints/tree/master/examples/HTML5ConstraintValidationAPI)

## How it works

The API works the same way as [React Router](https://reacttraining.com/react-router/web/example/basic):

```JSX


```

It is also inspired by [AngularJS ngMessages](https://docs.angularjs.org/api/ngMessages#usage).

If you had to implement validation yourself, you would end up with [a global object that tracks errors for each field](examples/PlainOldReact/App.tsx).
react-form-with-constraints [works similarly](packages/react-form-with-constraints/src/FieldsStore.ts).
It uses [React context](https://reactjs.org/docs/legacy-context.html) to share the [`FieldsStore`](packages/react-form-with-constraints/src/FieldsStore.ts) object across [`FieldFeedbacks`](packages/react-form-with-constraints/src/FieldFeedbacks.tsx) and [`FieldFeedback`](packages/react-form-with-constraints/src/FieldFeedback.tsx).

## API

The API reads like this: "for field when constraint violation display feedback", example:

```JSX


Should be at least 5 characters long

```

```
for field "password"
when constraint violation "valueMissing" display
when constraint violation "patternMismatch" display "Should be at least 5 characters long"
```

(\*) [element.validationMessage](https://www.w3.org/TR/html51/sec-forms.html#the-constraint-validation-api)

Async support works as follow:

```JSX

available ?
Username available :
Username already taken, choose another
// Why key=*? Needed otherwise React gets buggy when the user rapidly changes the field
}
/>

```

Trigger validation:

```JSX
function MyForm() {
const form = useRef(null);

async function handleChange({ target }) {
// Validates only the given fields and returns Promise
await form.current.validateFields(target);
}

async function handleSubmit(e) {
e.preventDefault();

// Validates the non-dirty fields and returns Promise
await form.current.validateForm();

if (form.current.isValid()) console.log('The form is valid');
else console.log('The form is invalid');
}

return (



Too short
available ?
Username available :
Username already taken, choose another
}
/>



);
}
```


**Important note:**

If a field (i.e an ``) does not have a matching `FieldFeedbacks`, the library won't known about this field (and thus won't perform validation).
The field name should match `FieldFeedbacks.for`:

```JSX

...

```




- [`FieldFeedbacks`](packages/react-form-with-constraints/src/FieldFeedbacks.tsx)

- `for: string` => reference to a `name` attribute (e.g ``), should be unique to the current form
- `stop?: 'first' | 'first-error' | 'first-warning' | 'first-info' | 'no'` =>
when to stop rendering `FieldFeedback`s, by default stops at the first error encountered (`FieldFeedback`s order matters)

Note: you can place `FieldFeedbacks` anywhere, have as many as you want for the same `field`, nest them, mix them with `FieldFeedback`... Example:

```JSX






...




...

```

- [`FieldFeedback`](packages/react-form-with-constraints/src/FieldFeedback.tsx)

- `when?`:
- [`ValidityState`](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) as a string => HTML5 constraint violation name
- `'*'` => matches any HTML5 constraint violation
- `'valid'` => displays the feedback only if the field is valid
- `(value: string) => boolean` => custom constraint
- `error?: boolean` => treats the feedback as an error (default)
- `warning?: boolean` => treats the feedback as a warning
- `info?: boolean` => treats the feedback as an info
- `children` => what to display when the constraint matches; if missing, displays the [HTML5 error message](https://www.w3.org/TR/html51/sec-forms.html#the-constraint-validation-api) if any

- [`Async`](packages/react-form-with-constraints/src/Async.tsx) => Async version of `FieldFeedback` (similar API as [react-promise](https://github.com/capaj/react-promise))

- `promise: (value: string) => Promise` => a promise you want to wait for
- `pending?: React.ReactNode` => runs when promise is pending
- `then?: (value: T) => React.ReactNode` => runs when promise is resolved
- `catch?: (reason: any) => React.ReactNode` => runs when promise is rejected

- [`FormWithConstraints`](packages/react-form-with-constraints/src/FormWithConstraints.tsx)

- `validateFields(...inputsOrNames: Array): Promise` =>
Should be called when a `field` changes, will re-render the proper `FieldFeedback`s (and update the internal `FieldsStore`).
Without arguments, all fields (`$('[name]')`) are validated.

- `validateFieldsWithoutFeedback(...inputsOrNames: Array): Promise` =>
Validates only all non-dirty fields (won't re-validate fields that have been already validated with `validateFields()`),
If you want to force re-validate all fields, use `validateFields()`.
Might be renamed to `validateNonDirtyFieldsOnly()` or `validateFieldsNotDirtyOnly()` in the future?

- `validateForm(): Promise` =>
Same as `validateFieldsWithoutFeedback()` without arguments, typically called before to submit the `form`.
Might be removed in the future?

- `isValid(): boolean` => should be called after `validateFields()`, `validateFieldsWithoutFeedback()` or `validateForm()`, indicates if the fields are valid

- `hasFeedbacks(): boolean` => indicates if any of the fields have any kind of feedback

- `resetFields(...inputsOrNames: Array): Field[]` =>
Resets the given fields and re-render the proper `FieldFeedback`s.
Without arguments, all fields (`$('[name]')`) are reset.

- [`Field`](packages/react-form-with-constraints/src/Field.ts) =>
```TypeScript
{
name: string;
validations: { // FieldFeedbackValidation[]
key: number;
type: 'error' | 'warning' | 'info' | 'whenValid';
show: boolean | undefined;
}[];
isValid: () => boolean
}
```

- [`Input`](packages/react-form-with-constraints/src/Input.tsx)

If you want to style ``, use `` instead: it will add classes `is-pending`, `has-errors`, `has-warnings`, `has-infos` and/or `is-valid` on `` when the field is validated.

Example: `` can generate ``

FYI `react-form-with-constraints-bootstrap` and `react-form-with-constraints-material-ui` already style the fields to match their respective frameworks.

## Browser support

react-form-with-constraints needs [`ValidityState`](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) which is supported by all modern browsers and IE 11.
It also needs a polyfill such as [core-js](https://github.com/zloirock/core-js) to support IE 11, see [React JavaScript Environment Requirements](https://reactjs.org/docs/javascript-environment-requirements.html).

You can use HTML5 attributes like `type="email"`, `required`, [`minlength`](https://caniuse.com/#feat=input-minlength)...

```JSX
Email

```

...and/or rely on `when` functions:

```JSX
Email

value.length === 0}>Please fill out this field.
!/\S+@\S+/.test(value)}>Invalid email address.

```

In the last case you will have to manage translations yourself (see SignUp example).

## Notes

- A [`type="hidden"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/hidden#Validation), [`readonly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) or [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-disabled) input won't trigger any HTML5 form constraint validation like [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-required), see https://codepen.io/tkrotoff/pen/gdjVNv