Upload - React Hook Form
A Upload component that uses react-hook-form Controller.
Installation
npx shadcn@latest add /registry/rhf-upload.json
Manual Installation
This component is dependent on upload component. Please add that component first before adding this one.
1. Copy and paste the following code into your project.
"use client";
import { useCallback } from "react";
import { FieldPath, Controller, FieldValues, useFormContext } from "react-hook-form";
import { Upload } from "@/components/ui/upload/upload";
import type { FileWithPathAndPreview } from "@/components/ui/upload/utils";
import { type FileWithPath } from "react-dropzone";
// ----------------------------------------------------------------------
type SingleUploadProps<TFieldValues extends FieldValues> = Omit<
React.ComponentPropsWithoutRef<typeof Upload>,
"file" | "files" | "onRemove" | "onRemoveAll" | "onUpload" | "onDrop"
> &
Omit<React.ComponentPropsWithoutRef<typeof Controller>, "name" | "control" | "render"> & {
name: FieldPath<TFieldValues>;
onDrop?: (file: FileWithPathAndPreview) => void;
onDelete?: (name: FieldPath<TFieldValues>) => void;
deletable?: boolean;
};
export function RHFUpload<TFieldValues extends FieldValues>({
name,
defaultValue,
rules,
deletable = false,
onDelete,
onDrop,
helperText,
...other
}: SingleUploadProps<TFieldValues>) {
const { control, setValue } = useFormContext();
const handleDrop = useCallback(
(acceptedFiles: FileWithPath[]) => {
if (acceptedFiles.length === 1) {
const file = Object.assign(acceptedFiles[0], {
preview: URL.createObjectURL(acceptedFiles[0]),
});
setValue(name, file as any);
if (!!onDrop) {
onDrop(file);
}
}
},
[onDrop, name, setValue],
);
const handleDelete = useCallback(() => {
setValue(name, defaultValue);
if (!!onDelete) {
onDelete(name);
}
}, [setValue, name, onDelete, defaultValue]);
return (
<Controller
name={name}
control={control}
defaultValue={defaultValue}
rules={rules}
render={({ field, fieldState: { error } }) => (
<Upload
multiple={false}
file={field.value}
error={!!error}
helperText={
(!!error || !!helperText) && (
<div className="mt-4 space-y-3">
{!!helperText && <p className="text-sm leading-none text-muted-foreground ml-1.5 mt-2">{helperText}</p>}
{!!error && <p className="text-sm leading-none text-error ml-1.5 mt-2">{error?.message}</p>}
</div>
)
}
onDrop={handleDrop}
onDelete={!!deletable || !!onDelete ? handleDelete : undefined}
{...other}
/>
)}
/>
);
}
2. Add this to your @/components/ui/hook-form/index.tsx
file for easy import.
export { default as RHFUpload } from "./rhf-upload";