RHFFileInput
File input component integrated with React Hook Form
You are reading the v1 docs. v2 renamed several props and replaced GeckoUIPortal with GeckoUIProvider. Read this page in v2.
Live examples are only rendered in the v2 docs.
RHFFileInput
A file input component integrated with React Hook Form that automatically creates preview URLs for selected files. Supports single and multiple file selection with custom rendering capabilities.
Installation
import { RHFFileInput } from '@geckoui/geckoui';Basic Usage
import { useForm, FormProvider } from 'react-hook-form';
import { RHFFileInput } from '@geckoui/geckoui';
function Example() {
const methods = useForm({
defaultValues: {
basicFile: undefined
}
});
return (
<FormProvider {...methods}>
<RHFFileInput name="basicFile" />
</FormProvider>
);
}Props API
Extends all props from standard HTML <input type="file"> element plus:
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | Field name (required) |
control | Control | Auto-injected | Optional: Pass explicitly for nested forms or custom form context |
rules | RegisterOptions | - | Inline validation rules |
multiple | boolean | false | Allow multiple file selection |
accept | string | - | File types to accept (e.g., "image/*", ".pdf") |
onChange | (data) => void | - | Change callback (receives file data, not event) |
onBlur | (event) => void | - | Blur callback |
render | ReactNode | Function | - | Custom render function with access to controller props |
className | string | - | Class for wrapper element |
inputClassName | string | - | Class for input element itself |
disabled | boolean | false | Disable file input |
| ...rest | InputHTMLAttributes | - | All other input attributes |
Examples
With Validation (Zod)
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFFileInput, RHFInputGroup, Button } from '@geckoui/geckoui';
const schema = z.object({
validationAvatar: z
.instanceof(File, { message: 'Please select a file' })
.refine((file) => file.size <= 5000000, 'File size must be less than 5MB')
.refine(
(file) => ['image/jpeg', 'image/png', 'image/webp'].includes(file.type),
'Only JPEG, PNG, and WebP images are allowed'
)
});
function Example() {
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: {
validationAvatar: undefined
}
});
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(console.log)}>
<RHFInputGroup label="Profile Picture" required>
<RHFFileInput name="validationAvatar" accept="image/*" />
</RHFInputGroup>
<Button type="submit">Upload</Button>
</form>
</FormProvider>
);
}Multiple Files
<RHFFileInput name="multipleImages" multiple accept="image/*" />Restrict File Types
<RHFFileInput name="pdfDocument" accept=".pdf" />Custom Render with Preview
<RHFFileInput
name="customAvatar"
accept="image/*"
render={({ field: { value } }) => {
return (
<div className="flex rounded overflow-hidden items-center justify-center cursor-pointer border-2 p-4 w-full border-dotted border-gray-300 h-[200px]">
{value ? (
<img
className="inline-block object-contain w-full h-full"
src={value.preview}
alt="Preview"
/>
) : (
<span>Click to Upload Image</span>
)}
</div>
);
}}
/>With onChange Callback
const handleFileChange = (data: FileWithPreview | FileWithPreview[]) => {
console.log('File selected:', data);
if (data && !Array.isArray(data)) {
alert(`File selected: ${data.name} (${(data.size / 1024).toFixed(2)} KB)`);
}
};
<RHFFileInput name="callbackFile" onChange={handleFileChange} />Complete Form Example
import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFFileInput, RHFInputGroup, Button } from '@geckoui/geckoui';
const schema = z.object({
resume: z
.instanceof(File, { message: 'Resume is required' })
.refine((file) => file.size <= 10000000, 'File size must be less than 10MB')
.refine(
(file) => ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'].includes(file.type),
'Only PDF and Word documents are allowed'
),
portfolio: z
.array(z.instanceof(File))
.min(1, 'At least one portfolio item is required')
.max(5, 'Maximum 5 files allowed')
.refine(
(files) => files.every((file) => file.size <= 5000000),
'Each file must be less than 5MB'
)
.refine(
(files) => files.every((file) => ['image/jpeg', 'image/png', 'image/webp'].includes(file.type)),
'Only JPEG, PNG, and WebP images are allowed'
)
});
function ApplicationForm() {
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
defaultValues: {
resume: undefined,
portfolio: undefined
}
});
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(console.log)}>
<RHFInputGroup label="Resume" required>
<RHFFileInput name="resume" accept=".pdf,.doc,.docx" />
</RHFInputGroup>
<RHFInputGroup label="Portfolio Images" required>
<RHFFileInput name="portfolio" multiple accept="image/*" />
</RHFInputGroup>
<Button type="submit">Submit Application</Button>
</form>
</FormProvider>
);
}Inline Rules Validation
For simple validation, use the rules prop:
<RHFFileInput
name="avatar"
rules={{ required: 'Please upload a file' }}
/>For complex forms, we recommend using a schema resolver (Zod, Yup) instead.
File Preview
RHFFileInput automatically creates preview URLs for selected files:
- Each file has a
previewproperty containing an Object URL - For single files:
value.preview - For multiple files:
value.map(file => file.preview) - Perfect for displaying image previews before upload
<RHFFileInput
name="image"
render={({ field: { value } }) => (
value && <img src={value.preview} alt="Preview" />
)}
/>File Types
The accept prop accepts standard HTML file input values:
accept="image/*"
accept=".pdf"
accept=".jpg,.jpeg,.png"
accept="video/*"
accept=".doc,.docx,application/pdf"Common MIME types:
- Images:
image/*,image/jpeg,image/png,image/webp - Documents:
application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document - Videos:
video/*,video/mp4
Validation with Zod
For single file:
z.instanceof(File, { message: 'File is required' })
.refine((file) => file.size <= 5000000, 'Max file size is 5MB')
.refine(
(file) => ['image/jpeg', 'image/png'].includes(file.type),
'Only JPEG and PNG allowed'
)For multiple files:
z.array(z.instanceof(File))
.min(1, 'At least one file required')
.max(5, 'Maximum 5 files')
.refine(
(files) => files.every((file) => file.size <= 5000000),
'Each file must be less than 5MB'
)Error States
RHFFileInput automatically displays error states with:
- Red border via
data-errorattribute on the root element when validation fails - Use with RHFInputGroup for label + error message display
.GeckoUIRHFFileInput[data-error] {
border-color: red;
}<RHFInputGroup label="Upload File" required>
<RHFFileInput name="file" />
</RHFInputGroup>Tip: Use mode: 'onBlur' in useForm to validate after file selection:
const methods = useForm({
resolver: zodResolver(schema),
mode: 'onBlur'
});Custom Rendering
The render prop provides full control over the UI while maintaining form integration:
<RHFFileInput
name="file"
render={({ field, fieldState, formState }) => (
<div>
{field.value && <p>{field.value.name}</p>}
{fieldState.error && <p>{fieldState.error.message}</p>}
</div>
)}
/>When using render, the default input is hidden but still functional.
Accessibility
- Standard HTML file input accessibility
- Keyboard navigable (Tab to focus, Enter/Space to open)
- Screen reader compatible
- Proper ARIA attributes for error states
Related Components
- RHFFilePicker - Drag-and-drop file picker with directory support
- RHFInputGroup - Label + input + error wrapper
- RHFError - Error message display