Validation
Formulier supports field-level validation. This means you provide each form field that needs validation with an optional validator function. This makes it easy to validate when the form has conditional fields or requires dynamic validation rules depending on other fields for example.
Example: Min Length
Let's validate that the firstName
and lastName
are at least 2 characters long.
We start with the form we made on the Getting Started page.
import * as React from 'react'
import {FormProvider, useCreateForm, useFormField, useFormInstance, useSubmitHandler} from '@formulier/react'
export default function Example() {
const form = useCreateForm({
initialValues: {
firstName: '',
lastName: '',
email: '',
},
})
const onSubmit = useSubmitHandler(form, values => {
alert(JSON.stringify(values, null, 2))
})
return (
<form onSubmit={onSubmit}>
<FormProvider form={form}>
<InputField name="firstName" label="First name" minLength={2} />
<InputField name="lastName" label="Last name" minLength={2} />
<InputField name="email" label="Email" type="email" />
<button type="submit">Submit</button>
</FormProvider>
</form>
)
}
function InputField({name, label, type = 'text', minLength}) {
const form = useFormInstance()
const validate = React.useCallback(
value => {
if (minLength !== undefined) {
if (typeof value === 'string' && value.length < minLength) {
return `Value should be at least ${minLength} characters long!`
}
}
return null
},
[minLength],
)
const [field, meta] = useFormField(form, {name, validate})
return (
<div className="field">
<label className="label" htmlFor={name}>
{label}
</label>
<input
{...field}
className="input"
type={type}
name={name}
value={field.value || ''}
onChange={event => field.onChange(event.target.value)}
/>
{meta.error && <span className="error">{meta.error}</span>}
</div>
)
}
Example: Required
Let's also make sure that all our fields are always filled out.
Continuing on where we left off:
import * as React from 'react'
import {FormProvider, useCreateForm, useFormField, useFormInstance, useSubmitHandler} from '@formulier/react'
export default function Example() {
const form = useCreateForm({
initialValues: {
firstName: '',
lastName: '',
email: '',
},
})
const onSubmit = useSubmitHandler(form, values => {
alert(JSON.stringify(values, null, 2))
})
return (
<form onSubmit={onSubmit}>
<FormProvider form={form}>
<InputField name="firstName" label="First name" minLength={2} required />
<InputField name="lastName" label="Last name" minLength={2} required />
<InputField name="email" label="Email" type="email" required />
<button type="submit">Submit</button>
</FormProvider>
</form>
)
}
function InputField({name, label, type = 'text', minLength, required}) {
const form = useFormInstance()
const validate = React.useCallback(
value => {
if (required) {
if (value === '') {
return 'Value is required!'
}
}
if (minLength !== undefined) {
if (typeof value === 'string' && value.length < minLength) {
return `Value should be at least ${minLength} characters long!`
}
}
return null
},
[required, minLength],
)
const [field, meta] = useFormField(form, {name, validate})
return (
<div className="field">
<label className="label" htmlFor={name}>
{label}
</label>
<input
{...field}
className="input"
type={type}
name={name}
value={field.value || ''}
onChange={event => field.onChange(event.target.value)}
/>
{meta.error && <span className="error">{meta.error}</span>}
</div>
)
}
Example: Results
Below you can see the result of our changes.
Validation Timing
validation
functions are called:
- When the user tries to submit the form
- When the field loses focus (Meaning the user "touched" the field)
- When the field is touched and the value changes (E.g. the user types a character in the input)
Example
Imagine the user focuses the firstName
field and tabs to the lastName
field. The firstName
field will now show the "Value is required!"
error. If the user then goes back to firstName
and types one character, the validation function is run again and now the error will be "Value should be at least 2 characters long!"
. Adding 2 more characters will make the error disappear.
Validation Order
It's important to note that a field can only have one error at a time. Therefore whichever error string is returned by our validate
function is the one that's available from meta.error
.
In our example above it makes sense to validate required
before minLength
so if we leave the field empty, we will see the required error instead of the min length one.