Search Components...

Upload Multiple - React Hook Form

A Upload Multiple component that uses react-hook-form Controller.

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 { Controller, type FieldValues, useFormContext, type FieldPath } 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 MultipleUploadProps<TFieldValues extends FieldValues> = Omit<React.ComponentPropsWithoutRef<typeof Upload>, "file" | "files" | "onDrop"> &
	Omit<React.ComponentPropsWithoutRef<typeof Controller>, "name" | "control" | "render"> & {
		name: FieldPath<TFieldValues>;
		onDrop?: (file: FileWithPath[]) => void;
		onRemoveAll?: () => void;
		onRemove?: (file: FileWithPathAndPreview | string, index: number) => void;
	};
 
export function RHFUploadMultiple<TFieldValues extends FieldValues>({
	name,
	defaultValue,
	rules,
	onDrop,
	onRemove,
	onRemoveAll,
	helperText,
	...other
}: MultipleUploadProps<TFieldValues>) {
	const { control, setValue, getValues } = useFormContext();
 
	const handleDrop = useCallback(
		(acceptedFiles: FileWithPath[]) => {
			const files = acceptedFiles.map((f) =>
				Object.assign(f, {
					preview: URL.createObjectURL(f),
				}),
			);
			setValue(name, files as any);
		},
		[name, setValue],
	);
 
	const handleRemove = useCallback(
		(_file: FileWithPathAndPreview | string, index: number) => {
			const values = getValues(name);
			if (!!values && Array.isArray(values)) {
				const newValues = values.toSpliced(index, 1);
				setValue(name, newValues);
			}
		},
		[setValue, name, getValues],
	);
 
	const handleRemoveAll = useCallback(() => {
		setValue(name, [] as any);
	}, [setValue, name]);
 
	return (
		<Controller
			name={name}
			control={control}
			defaultValue={defaultValue}
			rules={rules}
			render={({ field, fieldState: { error } }) => {
				return (
					<Upload
						multiple
						files={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={onDrop ?? handleDrop}
						onRemove={onRemove ?? handleRemove}
						onRemoveAll={onRemoveAll ?? handleRemoveAll}
						{...other}
					/>
				);
			}}
		/>
	);
}

2. Add this to your @/components/ui/hook-form/index.tsx file for easy import.

export { default as RHFUploadMultiple } from "./rhf-upload-multiple";