DEV Community

Cover image for Creating a Custom Styled File Input Component with Chakra UI and React Hook Form
Tashyn Wallace
Tashyn Wallace

Posted on

Creating a Custom Styled File Input Component with Chakra UI and React Hook Form

File uploads are a common feature in web applications, but customizing the look and behavior of a file input can be a challenge. In this tutorial, we'll walk you through creating a custom styled file input component using Chakra UI, React Hook Form, and React Dropzone. This component allows users to drag and drop files, ensuring a seamless and intuitive experience. The best part? It's easily adaptable to fit your specific requirements.

The Custom File Input Component:

import { useCallback, useState } from "react";
import { useDropzone } from "react-dropzone";
import {
  Center,
  useColorModeValue,
  Icon,
  VStack,
  Text,
  HStack,
  Spacer,
} from "@chakra-ui/react";
import { FiUploadCloud } from "react-icons/fi";
import { AiOutlineClose } from "react-icons/ai";
import { FieldError, FieldErrorsImpl, Merge } from "react-hook-form";

type Props = {
  onFileAccepted: any;
  formError: FieldError | Merge<FieldError, FieldErrorsImpl<any>> | undefined;
};

export default function FileUploadInput({ onFileAccepted, formError }: Props) {
  const [selectedFile, setSelectedFile] = useState<File>();
  const onDrop = useCallback(
    (acceptedFiles: any) => {
      setSelectedFile(acceptedFiles[0]);
      onFileAccepted(acceptedFiles[0]);
    },
    [onFileAccepted, selectedFile]
  );

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: {
      "application/pdf": [`.pdf`],
    },
    maxFiles: 1,
    multiple: false,
  });

  const activeBg = useColorModeValue("red.50", "red.100");
  const borderColor = useColorModeValue(
    isDragActive ? "red.300" : "gray.300",
    isDragActive ? "red.500" : "gray.500"
  );

  return (
    <Center
      p={10}
      cursor={!selectedFile ? "pointer" : "inherit"}
      w="100%"
      bg={isDragActive ? activeBg : "transparent"}
      _hover={{ bg: activeBg }}
      transition="background-color 0.2s ease"
      borderRadius={4}
      border={formError ? "2px" : "1px"}
      borderColor={formError ? "red.500" : borderColor}
      {...(!selectedFile ? getRootProps() : {})}
    >
      {!selectedFile ? (
        <>
          <input {...getInputProps()} />
          <VStack>
            <Icon as={FiUploadCloud} color="red.600" mr={2} boxSize={6} />
            <Text>
              {!isDragActive ? (
                <>
                  <Text as="span" fontWeight="semibold" color="pink.600">
                    Click to upload
                  </Text>
                  {` `}
                  or drag and drop
                </>
              ) : (
                `Drop the files here ...`
              )}
            </Text>
            <Text fontSize="sm">PDF Only</Text>
          </VStack>
        </>
      ) : (
        <>
          <HStack w="100%" p={5} borderRadius="md" bg="white">
            <Text>{selectedFile?.name}</Text>
            <Spacer />
            <Icon
              as={AiOutlineClose}
              onClick={() => {
                setSelectedFile(undefined);
                onFileAccepted(undefined);
              }}
              cursor="pointer"
            />
          </HStack>
        </>
      )}
    </Center>
  );
}

Enter fullscreen mode Exit fullscreen mode

The FileUploadInput component is the heart of this implementation. It utilizes React Dropzone for drag-and-drop functionality and Chakra UI for styling. This component also seamlessly integrates with React Hook Form, providing perfect type safety for your forms.

File Input in Action

Image description

Image description

Image description

Using the Custom Component in Your Form:

import { Controller } from "react-hook-form";
const {
  register,
  control,
  handleSubmit,
  reset,
  formState: { errors },
} = useForm<ImportDocumentType>({
  resolver: zodResolver(importDocumentSchema),
});

<FormControl isInvalid={Boolean(errors?.documentName)}>
  <FormLabel>Document Name</FormLabel>
  <Input {...register(`documentName`)} />
  <FormErrorMessage>
    {errors?.documentName && errors?.documentName?.message}
  </FormErrorMessage>
</FormControl>

<Divider />

<Controller
  name="file"
  control={control}
  render={({ field: { onChange } }) => (
    <FileUploadInput
      onFileAccepted={(file: any) => onChange(file)}
      formError={errors?.file}
    />
  )}
/>
Enter fullscreen mode Exit fullscreen mode

Here, we demonstrate how to incorporate the custom file input component into your form. It's as simple as using the Controller component from React Hook Form and passing in the FileUploadInput component as the render function. This allows you to manage file uploads with ease.

Customization and Adaptability:

One of the strengths of this approach is its flexibility. You can easily modify the styles, add animations, or tweak functionality to meet your specific project needs. The combination of Chakra UI and React Hook Form provides a powerful foundation for customization.

Top comments (0)