Handling file uploads is a common task in modern web development. Whether it is an insurance claim form or a job application, users need to upload documents.
In my eight years of working with React, I have seen many developers struggle with the default HTML file input. It is notoriously difficult to style and manage.
In this tutorial, I will show you exactly how to create a robust React file input component. We will cover everything from basic setups to advanced validation.
Set Up a Basic File Input in React
The simplest way to handle a file input in React is by using a functional component and the useState hook.
In this example, imagine we are building a portal for a US-based mortgage company where users need to upload a tax return document.
import React, { useState } from 'react';
const MortgageUpload = () => {
const [selectedFile, setSelectedFile] = useState(null);
const handleFileChange = (event) => {
// Accessing the first file from the file list
const file = event.target.files[0];
setSelectedFile(file);
console.log("Selected file:", file.name);
};
const handleUpload = () => {
if (selectedFile) {
alert(`Uploading ${selectedFile.name} to the mortgage portal...`);
// Here you would typically use FormData and fetch/axios to send data to a server
} else {
alert("Please select a file first.");
}
};
return (
<div style={{ padding: '20px', fontFamily: 'Arial' }}>
<h2>Mortgage Document Upload</h2>
<p>Please upload your 2023 Form 1040 (PDF format).</p>
<input type="file" onChange={handleFileChange} />
<div style={{ marginTop: '20px' }}>
<button onClick={handleUpload}>Submit Document</button>
</div>
{selectedFile && (
<div style={{ marginTop: '10px' }}>
<strong>Selected:</strong> {selectedFile.name}
</div>
)}
</div>
);
};
export default MortgageUpload;I executed the above example code and added the screenshot below.

When a user selects a file, the onChange event triggers. We then grab the file object from event.target.files.
Note that event.target.files is an array-like object. Even if you only pick one file, it is stored at index zero.
Use Refs for a Custom-Styled File Input
The default “Choose File” button provided by browsers rarely matches a professional UI design.
To fix this, I usually hide the actual input and trigger it using a custom button and a useRef hook.
Here is how you can build a custom-styled “Upload Resume” button for a tech job board in San Francisco.
import React, { useRef, useState } from 'react';
const JobApplication = () => {
const fileInputRef = useRef(null);
const [fileName, setFileName] = useState("No file chosen");
const handleClick = () => {
// Programmatically click the hidden file input
fileInputRef.current.click();
};
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
setFileName(file.name);
}
};
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h3>Apply for Senior Developer Position</h3>
{/* Hidden file input */}
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
style={{ display: 'none' }}
accept=".pdf,.docx"
/>
{/* Custom styled button */}
<button
onClick={handleClick}
style={{
backgroundColor: '#007bff',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
Upload Resume (PDF/DOCX)
</button>
<p style={{ fontStyle: 'italic', color: '#555' }}>{fileName}</p>
</div>
);
};
export default JobApplication;I executed the above example code and added the screenshot below.

Using display: ‘none’ on the input keeps it out of the layout. The ref allows the blue button to act as the trigger.
This method is my go-to because it provides complete control over the component’s visual presentation.
Handle Multiple File Uploads
Sometimes, you need to allow users to upload multiple documents at once. For example, a DMV portal might require a driver’s license scan and a utility bill.
To enable this, you simply add the multiple attribute to the input element.
import React, { useState } from 'react';
const DmvPortal = () => {
const [files, setFiles] = useState([]);
const handleFileChange = (event) => {
// Convert FileList to an array
const selectedFiles = Array.from(event.target.files);
setFiles(selectedFiles);
};
return (
<div style={{ padding: '30px', border: '1px solid #ccc', maxWidth: '500px' }}>
<h3>DMV Residency Verification</h3>
<label>Upload Proof of Residency (Multiple allowed):</label>
<br /><br />
<input
type="file"
multiple
onChange={handleFileChange}
/>
<div style={{ marginTop: '20px' }}>
<h4>Files to be uploaded:</h4>
<ul>
{files.map((file, index) => (
<li key={index}>
{file.name} ({(file.size / 1024).toFixed(2)} KB)
</li>
))}
</ul>
</div>
</div>
);
};
export default DmvPortal;I executed the above example code and added the screenshot below.

I recommend using Array.from() to convert the FileList into a standard JavaScript array.
This makes it much easier to use the .map() function to render the list of selected files in your UI.
Add File Type and Size Validation
Validation is critical. You don’t want a user uploading a 500MB video when you only asked for a 2MB profile picture.
In the US, many healthcare apps require specific image formats for insurance cards. Here is how I implement that check.
import React, { useState } from 'react';
const InsurancePortal = () => {
const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
const validateAndUpload = (event) => {
const file = event.target.files[0];
const MAX_SIZE = 2 * 1024 * 1024; // 2MB limit
const ALLOWED_TYPES = ['image/jpeg', 'image/png'];
setError("");
setSuccess(false);
if (!file) return;
if (!ALLOWED_TYPES.includes(file.type)) {
setError("Invalid file type. Please upload a JPG or PNG image.");
return;
}
if (file.size > MAX_SIZE) {
setError("File is too large. Max size is 2MB.");
return;
}
setSuccess(true);
console.log("Validation passed for:", file.name);
};
return (
<div style={{ padding: '20px' }}>
<h3>Insurance Card Photo</h3>
<input type="file" onChange={validateAndUpload} />
{error && <p style={{ color: 'red', fontWeight: 'bold' }}>{error}</p>}
{success && <p style={{ color: 'green' }}>File is ready for secure upload!</p>}
</div>
);
};
export default InsurancePortal;Validating the file.type and file.size properties locally saves the user from waiting for a failed server response.
Always remember that client-side validation is for user experience; you must still validate the file on your backend server for security.
Create a Drag and Drop File Input
Drag and drop is a high-end feature that users expect in modern SaaS applications.
You can build this without any external libraries by handling the onDragOver and onDrop events.
import React, { useState } from 'react';
const CloudStorage = () => {
const [dragActive, setDragActive] = useState(false);
const [fileName, setFileName] = useState("");
const handleDrag = (e) => {
e.preventDefault();
e.stopPropagation();
if (e.type === "dragenter" || e.type === "dragover") {
setDragActive(true);
} else if (e.type === "dragleave") {
setDragActive(false);
}
};
const handleDrop = (e) => {
e.preventDefault();
e.stopPropagation();
setDragActive(false);
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
setFileName(e.dataTransfer.files[0].name);
}
};
return (
<div
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
style={{
border: '2px dashed #999',
backgroundColor: dragActive ? '#e1f5fe' : '#fafafa',
padding: '40px',
textAlign: 'center',
borderRadius: '10px'
}}
>
<p>Drag and drop your project brief here</p>
{fileName && <p><strong>Uploaded:</strong> {fileName}</p>}
</div>
);
};
export default CloudStorage;The key here is calling e.preventDefault(). If you forget this, the browser will try to open the file in a new tab instead of letting your code handle it.
This pattern is great for clean, “Dropbox-style” interfaces that need to look modern and professional.
In this guide, we have explored how to handle single and multiple files, how to style inputs using refs, and how to implement drag-and-drop functionality.
You may also like to read:
- Build React Code Snippet Component
- How to Create a Retool Custom React Component
- How to Get Component Width in React
- Why is React Component Not Re-Rendering After State Update

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.