Validation & Error Messages
UniForm runs Zod validation through zodResolver from @hookform/resolvers. By default, Zod's own error messages are displayed. The messages prop lets you override them globally or per-field.
The messages prop
ValidationMessages supports three levels of override:
type ValidationMessages = {
/** Global override for any required-field error */
required?: string
/** Per-field overrides — string replaces all errors on that field,
* or an object maps Zod error codes to specific strings */
[fieldName: string]: string | Record<string, string> | undefined
}
Global required message
Override the message shown for any missing required value:
<AutoForm
messages={{ required: 'This field is required' }}
...
/>
This replaces errors for Zod's too_small (empty string, zero) and invalid_type (undefined on required field) codes.
Per-field string override
Replace all error messages for a specific field with a single string:
<AutoForm
messages={{
email: 'Please enter a valid email address',
username: 'Username is invalid',
}}
...
/>
Per-field per-code override
Map Zod error codes to specific messages for fine-grained control:
<AutoForm
messages={{
username: {
too_small: 'Username must be at least 3 characters',
too_big: 'Username cannot exceed 20 characters',
},
age: {
too_small: 'You must be at least 13 years old',
invalid_type: 'Age must be a number',
},
}}
...
/>
Common Zod error codes: too_small, too_big, invalid_type, invalid_string, invalid_enum_value.
Resolution order
messages[fieldName] accepts either a string (replaces all errors on that field) or an object (maps individual Zod error codes to strings). These are two alternative shapes — you choose one per field.
For each field error, UniForm resolves the message in this priority:
- Per-field string — if
messages[fieldName]is astring, it replaces every error on that field regardless of error code - Per-field per-code — if
messages[fieldName]is an object, the matchingmessages[fieldName][error.code]string is used - Global
messages.required— when the error is a required-field error (too_smallorinvalid_type) and no per-field override matched - Schema message — the message passed directly in the schema (e.g.
z.string().min(3, 'Too short!')) - Zod's default English message
Using with createAutoForm
Bake messages into a factory so all forms in your app share the same wording:
export const MyForm = createAutoForm({
messages: {
required: 'Required',
email: 'Invalid email address',
},
})
Live Example
const schema = z.object({ username: z.string().min(3).max(20), email: z.string().email(), age: z.number().min(13).max(120), }) const registrationForm = createForm(schema) function App() { const [result, setResult] = React.useState(null) return ( <div style={{ fontFamily: 'system-ui', maxWidth: 420 }}> <p style={{ fontSize: 13, color: 'var(--ifm-color-emphasis-600)', marginBottom: '1rem', }} > Try submitting with invalid values to see custom messages. </p> <AutoForm form={registrationForm} messages={{ required: 'This field cannot be empty', username: { too_small: 'Username needs at least 3 characters', too_big: 'Username cannot exceed 20 characters', }, email: "That doesn't look like a valid email", website: 'Please enter a full URL (https://…)', age: { too_small: 'You must be at least 13 years old', too_big: 'Age cannot exceed 120', invalid_type: 'Please enter a valid age', }, }} fields={{ username: { label: 'Username', description: '3–20 characters' }, email: { label: 'Email address' }, age: { label: 'Age' }, }} onSubmit={(v) => setResult(v)} /> {result && ( <pre style={{ marginTop: '1rem', background: 'var(--ifm-color-emphasis-200)', padding: '1rem', borderRadius: 6, }} > {JSON.stringify(result, null, 2)} </pre> )} </div> ) } render(<App />)