RHFError
Error message display component for React Hook Form validation
RHFError
A component for displaying form field error messages with React Hook Form. Automatically shows validation error messages for the specified field. Supports custom rendering and styling of error messages.
Installation
import { RHFError } from '@geckoui/geckoui';Basic Usage
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFInput, RHFError } from '@geckoui/geckoui';
const schema = z.object({
errorUsername: z.string().min(3, 'Username must be at least 3 characters')
});
function Example() {
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: { errorUsername: '' }
});
return (
<FormProvider {...methods}>
<div className="space-y-2">
<RHFInput name="errorUsername" placeholder="Enter username and blur to see error" />
<RHFError name="errorUsername" />
</div>
</FormProvider>
);
}Props API
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | Field name to display errors for (required) |
control | Control | Auto-injected | Optional: Pass explicitly for nested forms or custom form context |
className | string | - | Class name for error message (only when render is not provided) |
render | Function | - | Custom error rendering function |
Examples
With Custom Styling
<RHFInput name="styledEmail" placeholder="Enter email" />
<RHFError name="styledEmail" className="font-bold text-red-700 text-lg" />With Custom Render Function
const schema = z.object({
renderPassword: z.string().min(8, 'Password must be at least 8 characters')
});
<RHFInput name="renderPassword" type="password" placeholder="Enter password" />
<RHFError
name="renderPassword"
render={({ error }) => {
if (!error) return null;
return (
<div className="flex items-center space-x-2 bg-red-50 p-2 rounded">
<span className="text-red-500 text-xl">⚠️</span>
<span className="text-sm font-medium text-red-600">
{error?.message}
</span>
</div>
);
}}
/>Multiple Fields with Errors
const schema = z.object({
multiUsername: z.string().min(3, 'Username must be at least 3 characters'),
multiEmail: z.string().email('Invalid email address'),
multiPassword: z.string().min(8, 'Password must be at least 8 characters')
});
<div className="space-y-4">
<div className="space-y-1">
<label className="text-sm font-medium">Username</label>
<RHFInput name="multiUsername" placeholder="Enter username" />
<RHFError name="multiUsername" />
</div>
<div className="space-y-1">
<label className="text-sm font-medium">Email</label>
<RHFInput name="multiEmail" type="email" placeholder="Enter email" />
<RHFError name="multiEmail" />
</div>
<div className="space-y-1">
<label className="text-sm font-medium">Password</label>
<RHFInput name="multiPassword" type="password" placeholder="Enter password" />
<RHFError name="multiPassword" />
</div>
</div>With Icon
<RHFInput name="iconEmail" placeholder="Enter email" />
<RHFError
name="iconEmail"
render={({ error }) => {
if (!error) return null;
return (
<div className="flex items-center space-x-2">
<svg className="h-5 w-5 text-red-500" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<span className="text-sm text-red-600">{error?.message}</span>
</div>
);
}}
/>With Explicit Control Prop
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur'
});
<RHFInput name="controlField" placeholder="Enter text" />
<RHFError name="controlField" control={methods.control} />Complete Form Example
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFInput, RHFError, Button } from '@geckoui/geckoui';
const schema = z.object({
formErrorUsername: z.string().min(3, 'Username must be at least 3 characters'),
formErrorEmail: z.string().email('Invalid email address'),
formErrorPassword: z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[0-9]/, 'Password must contain at least one number'),
formErrorConfirm: z.string()
}).refine((data) => data.formErrorPassword === data.formErrorConfirm, {
message: "Passwords don't match",
path: ['formErrorConfirm']
});
function RegistrationForm() {
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: {
formErrorUsername: '',
formErrorEmail: '',
formErrorPassword: '',
formErrorConfirm: ''
}
});
const onSubmit = (data: any) => {
console.log('Form data:', data);
alert('Form submitted! Check console for data.');
};
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-1">
<label className="text-sm font-medium">Username *</label>
<RHFInput name="formErrorUsername" placeholder="Enter username" />
<RHFError name="formErrorUsername" />
</div>
<div className="space-y-1">
<label className="text-sm font-medium">Email *</label>
<RHFInput name="formErrorEmail" type="email" placeholder="Enter email" />
<RHFError name="formErrorEmail" />
</div>
<div className="space-y-1">
<label className="text-sm font-medium">Password *</label>
<RHFInput name="formErrorPassword" type="password" placeholder="Enter password" />
<RHFError name="formErrorPassword" />
</div>
<div className="space-y-1">
<label className="text-sm font-medium">Confirm Password *</label>
<RHFInput name="formErrorConfirm" type="password" placeholder="Confirm password" />
<RHFError name="formErrorConfirm" />
</div>
<Button type="submit">
Submit
</Button>
</form>
</FormProvider>
);
}How It Works
RHFError uses React Hook Form's Controller component internally to:
- Access field state - Gets the
fieldStatefor the specified field name - Check for errors - Returns
null(no render) if no error exists - Display error message - Shows
error.messagein anInputErrorcomponent - Support custom rendering - Allows complete control via the
renderprop
The component only renders when there's an error, making it efficient and clean.
Render Prop API
The render prop receives a ControllerFieldState object with:
{
error?: {
message?: string;
type?: string;
};
invalid: boolean;
isDirty: boolean;
isTouched: boolean;
}Example usage:
<RHFError
name="email"
render={({ error, invalid, isTouched }) => {
if (!error) return null;
return (
<div className={`error ${isTouched ? 'touched' : ''}`}>
{error.message}
</div>
);
}}
/>Usage Patterns
With RHFInputGroup
The easiest way to use RHFError is through RHFInputGroup, which automatically includes it:
<RHFInputGroup label="Email" required>
<RHFInput name="email" />
</RHFInputGroup>Manual Layout
For custom layouts, use RHFError manually:
<div className="field-container">
<Label htmlFor="email">Email Address</Label>
<RHFInput id="email" name="email" />
<RHFError name="email" />
</div>Multiple Errors
For fields with multiple validation rules, Zod returns the first error encountered:
const schema = z.object({
password: z.string()
.min(8, 'Minimum 8 characters')
.regex(/[A-Z]/, 'Must contain uppercase')
.regex(/[0-9]/, 'Must contain number')
});
<RHFError name="password" />Best Practices
-
Validation mode - Use
mode: 'onBlur'for better UX:const methods = useForm({ resolver: zodResolver(schema), mode: 'onBlur' // Shows errors after user leaves field }); -
Error messages - Write clear, actionable error messages:
z.string().min(8, 'Password must be at least 8 characters') // ✅ Clear and actionable z.string().min(8, 'Invalid') // ❌ Too vague -
Consistent styling - Use
classNamefor simple styling,renderfor complex designs:<RHFError name="email" className="text-sm text-red-600" /> -
Accessibility - Error messages are automatically associated with inputs when using RHFInputGroup
Accessibility
- Error messages are rendered in a
<p>tag with proper ARIA attributes via theInputErrorcomponent - Errors are announced to screen readers when they appear
- The
GeckoUIRHFErrorCSS class is applied for custom styling - Works seamlessly with keyboard navigation
Related Components
- RHFInputGroup - Layout component that includes RHFError
- RHFInput - Text input with React Hook Form
- RHFTextarea - Multi-line text input
- InputError - Base error message component