ReactHustle

How to Create a Custom React Tailwind File Upload Button

Jasser Mark Arioste

Jasser Mark Arioste

How to Create a Custom React Tailwind File Upload Button

Hello, hustlers! In this tutorial, you'll learn how to create custom file input buttons using React and TailwindCSS.

Introduction #

Styling the default <input type="file/> element can be a bit tricky especially if you're using TailwindCSS. In my knowledge, there are two ways to do this, one is to use the file: prefix of tailwind and to style default file input. Another way is to use multiple elements and react to completely change the look of the element while relying on hidden class.

Tutorial Objectives #

These are the two objectives of this tutorial and you'll have to decide what technique will fit your project.

  1. Learn how to style the default <input type="file"> component using React and Tailwind.
  2. Create a completely customized file input component where you can add icons and other text that you want.

Final Outputs #

Here are the final outputs of what we'll be making today.

Basic:

Basic File Input Styling with TailwindCSS

Advanced:

Advanced File upload Component using React and Tailwind

Example 1 - Styling the Default File Input element. #

In this step, we'll use the file: prefix to easily style the input element. First, let's the file components/FileInput.tsx

// components/FileInput.tsx
import classNames from "classnames"; // classNames to organize the classes.
import React, { ComponentPropsWithRef } from "react";

type Props = ComponentPropsWithRef<"input">;
const FileInput = (props: Props) => {
  return (
    <input
      {...props}
      className={classNames({
        // button colors
        "file:bg-violet-50 file:text-violet-500 hover:file:bg-violet-100": true,
        // button shape and spacing
        "file:rounded-lg file:rounded-tr-none file:rounded-br-none": true,
        "file:px-4 file:py-2 file:mr-4 file:border-none": true,
        // overall input styling
        "hover:cursor-pointer border rounded-lg text-gray-400": true,
      })}
      type="file"
    />
  );
};

export default FileInput;
123456789101112131415161718192021222324

Explanation:

First, we use the classnames package to organize the classes for better readability and maintainability.

In lines 12-15, we use the file: modifier since it allows you to style the button of the file input element. 

In line 17, we use the default tailwind classes to add a border to the input.

After this step, you'll get this beautiful file input component:

File Input Component styled with TailwindCSS

Example 2 - Creating a Custom File Input Component #

The previous example is a good way to style the default file input. However, what if you need custom text and an icon? It's very hard to do this using the previous example.

In this example, we'll create a component with the design of our choice. The downside of using this technique is that you can't completely rely on styling or TailwindCSS. You'll need javascript or React to handle the user input events.

First, let's create the file components/CustomFileInput.tsx:

// components/CustomFileInput.tsx
import React, { useRef } from "react";

const CustomFileInput = () => {
  const ref = useRef<HTMLInputElement>(null);
  return (
    <div>
      <input type="file" ref={ref} className="hidden" />
    </div>
  );
};

export default CustomFileInput;
12345678910111213

Explanation:

Here we use the hidden class to hide it from the user. We assign it a ref so that we can use ref.curent.click() later on to pass click events from another element or component.

Example 2 (Step 2) - Adding Styling and Elements #

Let's add some styling to the <div> element so that it looks like a button.

// components/CustomFileInput.tsx
import React, { useRef } from "react";

const CustomFileInput = () => {
  const ref = useRef<HTMLInputElement>(null);
  return (
    <div className="p-4 flex flex-col items-center gap-2 bg-violet-50 text-violet-500 rounded-lg hover:bg-violet-100 cursor-pointer">
      <input type="file" ref={ref} className="hidden" />
    </div>
  );
};

export default CustomFileInput;
12345678910111213

Next, let's add an icon using @heroicons/react and add a custom text.

// components/CustomFileInput.tsx
import React, { useRef } from "react";
import { CloudArrowUpIcon } from "@heroicons/react/24/outline";
const CustomFileInput = () => {
  const ref = useRef<HTMLInputElement>(null);
  return (
    <div className="p-4 flex flex-col items-center gap-2 bg-violet-50 text-violet-500 rounded-lg hover:bg-violet-100 cursor-pointer">
      <CloudArrowUpIcon className="w-6 h-6" />
      <span>Choose some files to upload</span>
      <input type="file" ref={ref} className="hidden" />
    </div>
  );
};

export default CustomFileInput;
123456789101112131415

Now, you have something like this but it doesn't work yet. 

Custom file input styled with TailwindCSS

Example 2 (Step 3) - Implementing the Correct Behavior #

Next, let's implement the correct behavior by passing the click events from the div element to the input element so that it will trigger the file selection. Copy the code below, I've added numbered comments so make sure you don't skip them.

// components/CustomFileInput.tsx
import React, { useRef, useState } from "react";
import { CloudArrowUpIcon } from "@heroicons/react/24/outline";
const CustomFileInput = () => {
  const ref = useRef<HTMLInputElement>(null);
  // 1. add state for tracking the selected files
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);

  // 2. pass the click event to the hidden input element to trigger the file selection.
  const handleClick = () => {
    ref.current?.click();
  };

  // 3. convert FileList to File[]
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.currentTarget.files ?? []);
    setSelectedFiles(files);
  };
  return (
    <div>
      <div
        // 4. add onClick handler
        onClick={handleClick}
        className="p-4 flex flex-col items-center gap-2 bg-violet-50 text-violet-500 rounded-lg hover:bg-violet-100 cursor-pointer"
      >
        <CloudArrowUpIcon className="w-6 h-6" />
        <span>Choose some files to upload</span>
        <input
          type="file"
          ref={ref}
          className="hidden"
          // 5. add onChange handler
          onChange={handleChange}
        />
      </div>
      {/* 6. display selected files */}
      {!!selectedFiles.length && (
        <div className="p-4 mt-4 bg-violet-50 overflow-hidden text-ellipsis">
          <p>Selected Files:</p>
          {selectedFiles.map((file, i) => {
            return (
              <span key={i} className="text-violet-500 whitespace-nowrap">
                {file.name}
              </span>
            );
          })}
        </div>
      )}
    </div>
  );
};

export default CustomFileInput;
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

After this step, you'll have a fully working custom file input handler implemented in React.

Full Code #

You can check out the full code on GitHub: jmarioste/react-tailwind-file-upload-button-tutorial

Conclusion #

You learned two ways how to customize the file input button using React and TailwindCSS. Tailwind gives us an easy way to customize it using the file: modifier. But combining it with React will allow us to completely create a new custom file input component.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on GitHub.

Resources #

Credits: Image by Joe from Pixabay

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle