React File Input Component

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.

React File Input Component

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.

File Input Component React

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.

File Input Component in React

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:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.