Messages and i18n
Custom error messages
You can send the error message you want with validators or manual errors but the browser is responsible for native errors.
The good thing is that they are already translated in the user language but sometimes you want to customize those error or just want the translation them to match the language chosen in your website.
In that case you can customize error messages using the messages
parameter:
import type { FormEvent } from 'react';
import type { IProps } from '../types';
import { type IFormValues, useForm } from '@per-form/react';
const messages = { valueMissing: 'Did you miss something ?' };
export default function Demo(props: IProps) {
function handleSubmit(_e: FormEvent<HTMLFormElement>, values: IFormValues) {
console.log(values);
}
const { errors, formProps } = useForm({
...props,
messages,
onSubmit: handleSubmit,
});
return (
<form {...formProps}>
<input name="text" required />
{errors.all.text && <div className="error">{errors.all.text}</div>}
<button type="submit">Submit</button>
</form>
);
}
Error keys
When using the messages
parameter you will have to use the following keys in your object:
- For validators, use the same string as the ones returned from your validators.
- Same for manual errors.
- For native errors, use
ValidityState
keys:badInput
: if the user has provided input that the browser is unable to convert.patternMismatch
: if the value does not match the specifiedpattern
attribute.rangeOverflow
: if the value is greater than the maximum specified by themax
attribute.rangeUnderflow
: if the value is less than the minimum specified by themin
attribute.stepMismatch
: if the value does not fit the rules determined by thestep
attribute.tooLong
: if the value exceeds the specifiedmaxlength
attribute.tooShort
: if the value fails to meet the specifiedminlength
attribute.typeMismatch
: if the value is not in the required syntax (when type isemail
orurl
).valueMissing
: if the element has arequired
attribute.
import { DatePicker } from '@mui/x-date-pickers';
import dayjs, { type Dayjs } from 'dayjs';
import { type FormEvent, useState } from 'react';
import type { IProps } from '../types';
import { type IFormValues, useForm } from '@per-form/react';
const defaultValues = { mui: null };
const validators = {
mui: (values: IFormValues) => {
const date = values.mui as Dayjs;
return date?.date() > 15 ? '' : 'dateUnderflow';
},
};
const messages = {
dateUnderflow: 'Choose a date after the 15th.',
minDate: 'The date must be greater than or equal to today.',
valueMissing: 'Did you miss something ?',
};
export default function Demo(props: IProps) {
const [value, setValue] = useState<Dayjs | null>(null);
function handleReset() {
setValue(null);
}
function handleSubmit(_e: FormEvent<HTMLFormElement>, values: IFormValues) {
console.log(values);
}
const { errors, formProps, onChange, onError } = useForm({
...props,
defaultValues,
messages,
onChangeOptOut: 'mui',
onReset: handleReset,
onSubmit: handleSubmit,
validators,
});
return (
<form {...formProps}>
<DatePicker
minDate={dayjs()}
name="mui"
onChange={onChange(setValue, { name: 'mui' })}
onError={onError('mui')}
slotProps={{ textField: { required: true } }}
value={value}
/>
{errors.all.mui && <div className="error">{errors.all.mui}</div>}
<div className="actions">
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</form>
);
}
By using this technique you centralize all translations in one place which is more convenient for i18n.
Local custom messages
You can also provide local custom messages using the useInput
or useInputs
hooks:
import type { FormEvent } from 'react';
import type { IFormValues } from '@per-form/react';
import type { IProps } from '../types';
import { FormProvider, useForm, useInput } from '@per-form/react';
const validator = (values: IFormValues) =>
String(values.text).includes('foo') ? '' : 'fooError';
const globalMessages = { valueMissing: 'did you miss something ?' };
const localMessages = { fooError: 'Value does not include "foo"' };
function Input() {
const { errors } = useInput({
messages: localMessages,
name: 'text',
validator,
});
return (
<>
<input name="text" required />
{errors.all.text && <div className="error">{errors.all.text}</div>}
</>
);
}
export default function Demo(props: IProps) {
function handleSubmit(_e: FormEvent<HTMLFormElement>, values: IFormValues) {
console.log(values);
}
const { formProps, ...context } = useForm({
...props,
messages: globalMessages,
onSubmit: handleSubmit,
});
return (
<FormProvider {...context}>
<form {...formProps}>
<Input />
<button type="submit">Submit</button>
</form>
</FormProvider>
);
}
Global messages are also available for locally declared fields, but local messages will only apply to associated fields.
If global and local keys conflict, local messages will take precedence over global messages.