How to Upload Files in React JS

Adding file uploads to a React app sounds simple until you build one for a real dashboard. A user selects a profile image, invoice, or project document, but the file name does not appear, validation feels messy, and your API receives an empty request.

I have built upload screens for admin panels and task management apps where users attach documents to tasks. The key is to keep the selected file in component state, validate it before upload, and send it with FormData.

You will learn how to upload files in React JS with a clean functional component, file validation, previews, multiple-file handling, and an API request.

How to Upload Files in React JS

A file upload has two separate parts:

  • The browser lets the user choose a file through an HTML file input.
  • Your React app sends that file to a server endpoint.

React does not upload the file automatically when a user selects it. The <input type="file"> element only gives your component access to a browser File object. You decide when and how to send it.

For this guide, I will use a task dashboard example. A manager creates a task and attaches a specification document or image.

Before you start, make sure you are using a modern React version with functional components and useState. If you need a refresher on setting up a project, see this guide on creating your first React app.

Create a Basic File Input

Start with a component that lets users select one file.

import { useState } from "react";

export default function TaskFileUpload() {
const [selectedFile, setSelectedFile] = useState(null);

function handleFileChange(event) {
const file = event.target.files[0];
setSelectedFile(file);
}

return (
<section>
<h2>Attach a task file</h2>

<input
type="file"
onChange={handleFileChange}
/>

{selectedFile && (
<p>Selected file: {selectedFile.name}</p>
)}
</section>
);
}

The useState hook creates selectedFile, which stores the currently chosen file. Its initial value is null because the user has not selected anything yet.

The browser provides selected files through event.target.files. This value is a FileList, not a normal JavaScript array. Since this example accepts one file, event.target.files[0] gets the first item.

The onChange handler runs whenever the user picks a file. React then updates the state and renders the file name below the input. This event pattern also applies when you handle events in React JS.

You should not try to control a file input by setting its value from React state. Browsers protect file inputs for security reasons. Let the browser manage the input value, then store the selected File object in React state.

Validate the File Before Upload

A file input can accept nearly any file unless you add rules. In a real app, validate file type and size before sending anything to the server.

For example, a task dashboard may allow PDF, PNG, and JPEG attachments up to 5 MB.

import { useState } from "react";

const allowedTypes = ["application/pdf", "image/png", "image/jpeg"];
const maxFileSize = 5 * 1024 * 1024;

export default function TaskFileUpload() {
const [selectedFile, setSelectedFile] = useState(null);
const [error, setError] = useState("");

function handleFileChange(event) {
const file = event.target.files[0];

if (!file) {
return;
}

if (!allowedTypes.includes(file.type)) {
setSelectedFile(null);
setError("Choose a PDF, PNG, or JPG file.");
return;
}

if (file.size > maxFileSize) {
setSelectedFile(null);
setError("Choose a file smaller than 5 MB.");
return;
}

setError("");
setSelectedFile(file);
}

return (
<section>
<label htmlFor="task-file">Task attachment</label>

<input
id="task-file"
type="file"
accept=".pdf,.png,.jpg,.jpeg"
onChange={handleFileChange}
/>

{error && <p role="alert">{error}</p>}

{selectedFile && (
<p>Ready to upload: {selectedFile.name}</p>
)}
</section>
);
}

The accept attribute improves the file-picker experience by showing preferred file types. It does not provide real security. A user can still bypass it, so your React validation helps the user, while server-side validation protects your application.

This component uses props only indirectly through standard HTML attributes. When you split this UI into reusable child components, pass file details and callback functions as props. You can learn more about using props in React JS.

The error state keeps the validation message separate from the file. This makes the rendering logic clear: show an error when one exists, or show the selected file when it passes validation.

Upload Files in React JS with FormData

Once you have a valid file, use FormData to send it to your API. FormData is a browser object that packages files and regular form fields into a request format that servers understand.

Here is a complete example with an Upload button.

import { useState } from "react";

export default function TaskFileUpload() {
const [selectedFile, setSelectedFile] = useState(null);
const [status, setStatus] = useState("");

function handleFileChange(event) {
const file = event.target.files[0] || null;
setSelectedFile(file);
setStatus("");
}

async function handleUpload() {
if (!selectedFile) {
setStatus("Select a file before uploading.");
return;
}

const formData = new FormData();
formData.append("attachment", selectedFile);
formData.append("taskId", "task-104");

try {
setStatus("Uploading...");

const response = await fetch("/api/tasks/attachments", {
method: "POST",
body: formData,
});

if (!response.ok) {
throw new Error("Upload failed.");
}

const result = await response.json();

setStatus(`Uploaded: ${result.fileName}`);
setSelectedFile(null);
} catch (error) {
setStatus("The file could not be uploaded. Try again.");
}
}

return (
<section>
<label htmlFor="task-file">Task attachment</label>

<input
id="task-file"
type="file"
onChange={handleFileChange}
/>

{selectedFile && <p>{selectedFile.name}</p>}

<button type="button" onClick={handleUpload}>
Upload file
</button>

{status && <p role="status">{status}</p>}
</section>
);
}

You can refer to the screenshot below to see the output.

Upload Files in React JS

The formData.append("attachment", selectedFile) line adds the actual file. The string "attachment" is the field name your server expects. If your backend expects "file" instead, use "file" in that line.

The second append() call adds the task ID. This lets the server know which task should own the uploaded file. You can add other values the same way, such as a project ID, description, or category.

Do not manually add a Content-Type header when you send FormData. The browser adds the correct multipart boundary automatically. A manually set header often causes upload requests to fail because it misses that boundary.

The try...catch block handles network problems. The response.ok check handles server responses such as 400 or 500 errors. These two checks make upload feedback much more useful in an admin panel.

Show an Image Preview Before Upload

Image previews help users confirm they picked the correct file. This works especially well for profile photos, task screenshots, product images, and support attachments.

Use URL.createObjectURL() to create a temporary browser URL for the selected image.

import { useState } from "react";

export default function ImageUpload() {
const [imageFile, setImageFile] = useState(null);
const [previewUrl, setPreviewUrl] = useState("");

function handleImageChange(event) {
const file = event.target.files[0];

if (!file) {
return;
}

if (!file.type.startsWith("image/")) {
setImageFile(null);
setPreviewUrl("");
return;
}

setImageFile(file);
setPreviewUrl(URL.createObjectURL(file));
}

return (
<section>
<label htmlFor="task-image">Upload task image</label>

<input
id="task-image"
type="file"
accept="image/*"
onChange={handleImageChange}
/>

{previewUrl && (
<img
src={previewUrl}
alt={`Preview of ${imageFile.name}`}
width="240"
/>
)}
</section>
);
}

The startsWith("image/") check rejects non-image files before React creates a preview. This prevents a PDF or spreadsheet from being used as an image source.

The preview URL only exists in the browser. It does not mean the image has reached your server. The file still needs a FormData upload request when the user clicks Save or Upload.

For a dedicated implementation pattern, see this guide on building a React image upload component.

Upload Multiple Files in React JS

Many task apps allow users to attach several files at once. Add the multiple attribute, then convert the FileList into a JavaScript array.

import { useState } from "react";

export default function MultipleTaskFiles() {
const [files, setFiles] = useState([]);

function handleFilesChange(event) {
const selectedFiles = Array.from(event.target.files);
setFiles(selectedFiles);
}

return (
<section>
<label htmlFor="task-files">Attach files</label>

<input
id="task-files"
type="file"
multiple
onChange={handleFilesChange}
/>

<ul>
{files.map((file) => (
<li key={`${file.name}-${file.lastModified}`}>
{file.name}
</li>
))}
</ul>
</section>
);
}

You can refer to the screenshot below to see the output.

How to Upload Files in React JS

Array.from() converts the browser FileList into a real array. Once it becomes an array, you can use JavaScript methods like map(), filter(), and find().

The key prop gives each rendered list item a stable identity. Here, the file name and last-modified timestamp create a practical key for a temporary client-side list. Do not use the array index when users can remove or reorder files.

To send multiple files, append each one to the same FormData object.

async function handleUploadAll() {
const formData = new FormData();

files.forEach((file) => {
formData.append("attachments", file);
});

await fetch("/api/tasks/attachments", {
method: "POST",
body: formData,
});
}

Each append() call adds another file under the attachments field. Your backend must support multiple files with that field name.

Remove a Selected File Safely

Users often select the wrong document. Give them a way to remove a file before uploading it.

function removeFile(fileToRemove) {
setFiles((currentFiles) =>
currentFiles.filter((file) => file !== fileToRemove)
);
}

This uses filter() to create a new array without the chosen file. It follows immutability, which means you create new state values instead of changing existing ones.

Avoid code like files.splice(index, 1) followed by setFiles(files). splice() changes the original array. React state updates become harder to predict when you mutate an array in place.

You can add a remove button beside each file:

{files.map((file) => (
<li key={`${file.name}-${file.lastModified}`}>
{file.name}
<button type="button" onClick={() => removeFile(file)}>
Remove
</button>
</li>
))}

This approach works well in a reusable React file input component, where a parent component owns the uploaded-file list.

Pro Tip: I’ve found that resetting the file input after a successful upload prevents a common bug where selecting the same file again does not trigger onChange. Keep a ref to the input and set inputRef.current.value = "" only after you have cleared the related React state.

Things to Keep in Mind

  • Validate on the server: Client-side checks improve the experience, but the server must verify file type, size, ownership, and permissions before storing a file.
  • Do not set Content-Type manually: When using FormData, let the browser add the multipart request header and boundary.
  • Keep File objects in state: Store the selected File object, not only its name, because you need the file itself for the upload request.
  • Use immutable updates: Create a new array with filter(), the spread operator, or map() when adding or removing selected files.
  • Use stable key props: A file list needs a reliable key prop so React can update the correct row when users remove attachments.
  • Clean up image previews: If users frequently replace images, revoke old object URLs to avoid keeping unnecessary browser memory.

Frequently Asked Questions

How do I upload files in React JS?

Use an <input type="file"> element to let users choose a file. Save event.target.files[0] in useState, add it to FormData, and send it in a POST request with fetch().

Why is my uploaded file missing from FormData?

You may be appending the file name instead of the actual File object. Use formData.append("attachment", selectedFile), where selectedFile comes from the file input event.

Can I upload multiple files in React?

Yes. Add the multiple attribute to the input, convert event.target.files with Array.from(), and append every file to FormData in a loop.

Should I use useState for file uploads?

Use useState to store selected files, errors, upload status, and preview URLs. Do not try to control the file input value through state because browsers restrict that behavior.

How do I validate a file before uploading it?

Check file.type for the allowed MIME types and file.size for a size limit. Show a helpful message immediately, but repeat the validation on your server.

Can I preview an image before uploading it?

Yes. Create a temporary preview URL with URL.createObjectURL(file) and use it as an image src. The preview stays in the browser until you upload the actual file through your API.

File uploads in React become much easier when you separate selection, validation, preview, and server upload into clear steps. Store the File object in state, use FormData for the request, and keep every array update immutable when handling multiple attachments.

You May Also Like

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.