
Security News
The Changelog Podcast: Practical Steps to Stay Safe on npm
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.
react-form-with-constraints
Advanced tools
Simple form validation for React
npm install react-form-with-constraintsCheck the changelog for breaking changes and fixes between releases.
⚠️ Client side validation is cosmetic, you should not rely on it to enforce security
<form>
<label for="email">Email:</label>
<input type="email" id="email" required>
<button type="submit">Submit</button>
</form>

The required HTML5 attribute specifies that the user must fill in a value, type="email" checks that the entered text looks like an email address.
Resources:
<FieldFeedback when="valueMissing">My custom error message</FieldFeedback><FieldFeedback when={value => ...}><FieldFeedback ... warning>, <FieldFeedback ... info>react-form-with-constraints-bootstrapreact-form-with-constraints-material-uireact-form-with-constraints-native<input type="password" name="password"
value={this.state.password} onChange={this.handleChange}
required pattern=".{5,}" />
<FieldFeedbacks for="password">
<FieldFeedback when="valueMissing" />
<FieldFeedback when="patternMismatch">
Should be at least 5 characters long
</FieldFeedback>
<FieldFeedback when={value => !/\d/.test(value)} warning>
Should contain numbers
</FieldFeedback>
<FieldFeedback when={value => !/[a-z]/.test(value)} warning>
Should contain small letters
</FieldFeedback>
<FieldFeedback when={value => !/[A-Z]/.test(value)} warning>
Should contain capital letters
</FieldFeedback>
</FieldFeedbacks>
CodePen basic Password example: https://codepen.io/tkrotoff/pen/BRGdqL (StackBlitz version)

React Native example (React classes):
| iOS | Android |
|---|---|
![]() | ![]() |
Other examples from the examples directory:
The API works the same way as React Router:
<Router>
<Route exact path="/" component={Home} />
<Route path="/news" component={NewsFeed} />
</Router>
It is also inspired by AngularJS ngMessages.
If you had to implement validation yourself, you would end up with a global object that tracks errors for each field.
react-form-with-constraints works similarly.
It uses React context to share the FieldsStore object across FieldFeedbacks and FieldFeedback.
The API reads like this: "for field when constraint violation display feedback", example:
<FieldFeedbacks for="password">
<FieldFeedback when="valueMissing" />
<FieldFeedback when="patternMismatch">Should be at least 5 characters long</FieldFeedback>
</FieldFeedbacks>
for field "password"
when constraint violation "valueMissing" display <the HTML5 error message (*)>
when constraint violation "patternMismatch" display "Should be at least 5 characters long"
Async support works as follow:
<FieldFeedbacks for="username">
<Async
promise={checkUsernameAvailability} /* Function that returns a promise */
then={available => available ?
<FieldFeedback key="1" info style={{color: 'green'}}>Username available</FieldFeedback> :
<FieldFeedback key="2">Username already taken, choose another</FieldFeedback>
// Why key=*? Needed otherwise React gets buggy when the user rapidly changes the field
}
/>
</FieldFeedbacks>
Trigger validation:
function MyForm() {
const form = useRef(null);
async function handleChange({ target }) {
// Validates only the given fields and returns Promise<Field[]>
await form.current.validateFields(target);
}
async function handleSubmit(e) {
e.preventDefault();
// Validates the non-dirty fields and returns Promise<Field[]>
await form.current.validateForm();
if (form.current.isValid()) console.log('The form is valid');
else console.log('The form is invalid');
}
return (
<FormWithConstraints ref={form} onSubmit={handleSubmit} noValidate>
<input
name="username"
onChange={handleChange}
required minLength={3}
/>
<FieldFeedbacks for="username">
<FieldFeedback when="tooShort">Too short</FieldFeedback>
<Async
promise={checkUsernameAvailability}
then={available => available ?
<FieldFeedback key="1" info style={{color: 'green'}}>Username available</FieldFeedback> :
<FieldFeedback key="2">Username already taken, choose another</FieldFeedback>
}
/>
<FieldFeedback when="*" />
</FieldFeedbacks>
</FormWithConstraints>
);
}
Important note:
If a field (i.e an <input>) 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:
<input name="MY_FIELD" ...>
<FieldFeedbacks for="MY_FIELD">
...
</FieldFeedbacks>
for: string => reference to a name attribute (e.g <input name="username">), should be unique to the current formstop?: 'first' | 'first-error' | 'first-warning' | 'first-info' | 'no' =>
when to stop rendering FieldFeedbacks, by default stops at the first error encountered (FieldFeedbacks 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:
<input name="username" ... />
<FieldFeedbacks for="username" stop="first-warning">
<FieldFeedbacks>
<FieldFeedback ... />
<Async ... />
<FieldFeedbacks stop="first-info">
...
</FieldFeedbacks>
</FieldFeedbacks>
<FieldFeedback ... />
<Async ... />
</FieldFeedbacks>
<FieldFeedbacks for="username" stop="no">
...
</FieldFeedbacks>
when?:
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 constrainterror?: boolean => treats the feedback as an error (default)warning?: boolean => treats the feedback as a warninginfo?: boolean => treats the feedback as an infochildren => what to display when the constraint matches; if missing, displays the HTML5 error message if anyAsync<T> => Async version of FieldFeedback (similar API as react-promise)
promise: (value: string) => Promise<T> => a promise you want to wait forpending?: React.ReactNode => runs when promise is pendingthen?: (value: T) => React.ReactNode => runs when promise is resolvedcatch?: (reason: any) => React.ReactNode => runs when promise is rejectedvalidateFields(...inputsOrNames: Array<Input | string>): Promise<Field[]> =>
Should be called when a field changes, will re-render the proper FieldFeedbacks (and update the internal FieldsStore).
Without arguments, all fields ($('[name]')) are validated.
validateFieldsWithoutFeedback(...inputsOrNames: Array<Input | string>): Promise<Field[]> =>
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<Field[]> =>
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<Input | string>): Field[] =>
Resets the given fields and re-render the proper FieldFeedbacks.
Without arguments, all fields ($('[name]')) are reset.
Field =>
{
name: string;
validations: { // FieldFeedbackValidation[]
key: number;
type: 'error' | 'warning' | 'info' | 'whenValid';
show: boolean | undefined;
}[];
isValid: () => boolean
}
If you want to style <input>, use <Input> instead: it will add classes is-pending, has-errors, has-warnings, has-infos and/or is-valid on <input> when the field is validated.
Example: <Input name="username" /> can generate <input name="username" class="has-errors has-warnings">
FYI react-form-with-constraints-bootstrap and react-form-with-constraints-material-ui already style the fields to match their respective frameworks.
react-form-with-constraints needs ValidityState which is supported by all modern browsers and IE 11.
It also needs a polyfill such as core-js to support IE 11, see React JavaScript Environment Requirements.
You can use HTML5 attributes like type="email", required, minlength...
<label htmlFor="email">Email</label>
<input type="email" name="email" id="email"
value={this.state.email} onChange={this.handleChange}
required />
<FieldFeedbacks for="email">
<FieldFeedback when="*" />
</FieldFeedbacks>
...and/or rely on when functions:
<label htmlFor="email">Email</label>
<input name="email" id="email"
value={this.state.email} onChange={this.handleChange} />
<FieldFeedbacks for="email">
<FieldFeedback when={value => value.length === 0}>Please fill out this field.</FieldFeedback>
<FieldFeedback when={value => !/\S+@\S+/.test(value)}>Invalid email address.</FieldFeedback>
</FieldFeedbacks>
In the last case you will have to manage translations yourself (see SignUp example).
type="hidden", readonly or disabled input won't trigger any HTML5 form constraint validation like required, see https://codepen.io/tkrotoff/pen/gdjVNvFAQs
Simple form validation for React
We found that react-form-with-constraints demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.

Security News
Experts push back on new claims about AI-driven ransomware, warning that hype and sponsored research are distorting how the threat is understood.

Security News
Ruby's creator Matz assumes control of RubyGems and Bundler repositories while former maintainers agree to step back and transfer all rights to end the dispute.