How to Create a Custom React Tailwind File Upload Button
Jasser Mark Arioste
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.
- Learn how to style the default
<input type="file">
component using React and Tailwind. - 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:
Advanced:
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:
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.
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