How to Upload a File Using React

Learn how to upload files using the react JavaScript Framework

If you are creating web apps and tools, you come to a point where you need to upload a file from users browser to an server. That's what happened to me recently. For a new tool, I was looking for an easy way to upload files using React. While I discovered different examples, components and libraries, there was no simple lightweight example that started with the basics.

Here you will learn the basics of how to simply upload a file using plain react, build a file upload component, and explore possibilities using existing components and libraries.

I assume that you know how to upload a file using HTML and further process it with Javascript. If not, you will find all the necessary information here.

The Basics

Let's start with a simple example of a React Component that processes a user-selected file and prints useful information like name, size, and type of the file.

Select and Process

First, we need an HTML input element and a change handler that processes the file. Then we can use Reacts Component State to re-render the Component with the file information.

import {useState} from 'react';
export default function FileUpload() {
const [file, setFile] = useState();
const fileChangeHandler = (event) => {
// file now contains useful information of the file like name, type and size
const file = event.target.files[0];
console.log(file);
setFile(file);
// to access the files content you need the FileReader object
const reader = new FileReader()
reader.onabort = () => console.error('Reading the file aborted.')
reader.onerror = () => console.error('Error reading the file.')
reader.onload = (event) => {
const binaryString = event.target.result;
console.log(binaryString);
}
reader.readAsBinaryString(file);
};
return (
<div>
<h1>React File Upload</h1>
<input type="file" onChange={fileChangeHandler} />
{!file && (
<div>
<p>Please select a file.</p>
</div>
)}
{file && (
<div>
<p>File with name <strong>{file.name}</strong> of type <strong>{file.type}</strong> and <strong>{file.size}</strong> bytes length selected.</p>
</div>
)}
</div>
)
}

If you want to start from scratch, follow these steps:

  1. Add a file input element: <input type="file" />
  2. Create a fileChangeHandler method and read file information from event.target.files[0]
  3. Store the file in component’s state and add file information to html
  4. Use the FileReader object to access and process the content of the user-selected file

Preview contents

The FileReader object has another interesting method readAsDataURL where you get the file contents as Base64-URL. This is very useful for a preview. Another option without FileReader would be using the URL.createObjectURL(object) method and event.target.files[0] as argument:

import {useState} from 'react';
export default function FileUpload() {
const [image, setImage] = useState();
const fileChangeHandler = (event) => {
const url = URL.createObjectURL(event.target.files[0], { oneTimeOnly: true });
setImage(url);
};
return (
<div>
<h1>React File Upload</h1>
<input type="file" onChange={fileChangeHandler} />
{!image && (
<div>
<p>Please select a file.</p>
</div>
)}
{image && (
<div>
<p>Image selected:</p>
<img src={image} />
</div>
)}
</div>
)
}

But wait, that's not a File Upload! You are right! Now that we know how to process and work with the file's data, we can use this information to upload the file.

Upload the File

The file upload happens between the browser and a remote server and can easily be done using the Javascript fetch method. We use the FormData object to make it easier to process the file in the backend.

Add the following code in your fileChangeHandler after the line setFile(file);

const formData = new FormData();
formData.append("file", file);
// Use the Fetch API to upload the file to any server
fetch('https://httpbin.org/post', {
method: 'POST',
body: formData,
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));

You can also use Axios to upload the file. Axios is a promise-based HTTP client with support for upload progress and many other features that are missing from fetch.

If you haven't already, install Axios first:

npm install --save axios

..and import it in the top of your file..

import axios from "axios";

Then we can use it in our FileUplaod component:

const formData = new FormData();
formData.append("file", file);
// Use Axios to upload the file to any server
axios.post('https://httpbin.org/post',
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
)
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));

Show a progress bar

If you expect large files or slow internet connections, it makes sense to show a progress bar while uploading. With Axios you can use the onUploadProgress method as config option to calculate how many percent the upload is completed. The method gives us the loaded and total vars with the progressEvent object as function argument.

Here is the full example with the a progress bar:

import {useState} from 'react';
import axios from "axios";
export default function FileUpload() {
const [file, setFile] = useState();
const [progress, setProgress] = useState(0);
const fileChangeHandler = (event) => {
// file now contains useful information of the file like name, type and size
const file = event.target.files[0];
// set file state
setFile(file);
// create FormData object to prepare file for upload
const formData = new FormData();
formData.append("file", file);
// Use Axios to upload the file to any server
axios
.post("https://httpbin.org/post", formData, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: function (progressEvent) {
// Calculate percent completed. Axios gives us the loaded and total vars with the progressEvent.
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
// set progress state to change progress bar width and text
setProgress(percentCompleted);
}
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));
};
return (
<div>
<h1>React File Upload</h1>
<input type="file" onChange={fileChangeHandler} />
{!file && (
<div>
<p>Please select a file.</p>
</div>
)}
{file && (
<div>
<p>
File with name <strong>{file.name}</strong> of type{" "}
<strong>{file.type}</strong> and <strong>{file.size}</strong> bytes
length selected.
</p>
<div
style={{
width: progress + "%",
height: "20px",
backgroundColor: "#33ff88",
}}
></div>
Upload: {progress}% Completed
</div>
)}
</div>
);
}

From here you have countless options to improve and extend the file upload. For example: you can create a thumbnail for preview before uploading, add a (better) Progress Bar, and so on.

Exiting Components

If you don't want to start from scratch or need more options, you can use existing components. I've found some great components, that I like to share with you:

react-dropzone

Do you like dragging files around? Then you are right here. 😀 As the name suggests, react-dropzone is a component to create a drag'n'drop zone for files. The user can drag a file (or multiple files) from their computer into the browser and the drop zone and the file(s) will then be uploaded directly. The component is easy to use and can be used to quickly create beautiful drop zones.

To use the component, install it from npm:

npm install --save react-dropzone

Then we can include it in our FileUpload component, define a drop handler and add the wrapper to our JSX.

import React, { useCallback } from "react";
import {useDropzone} from 'react-dropzone'
const FileLabel = function(props) {
const hasFiles = props.fileNames.length > 0;
if (hasFiles) {
return <p>{props.fileNames.join(", ")}</p>
}
return <p>Drag and drop a file here, or click to select file.</p>;
}
export default function FileUpload(props) {
const onDrop = useCallback((acceptedFiles) => {
typeof props.uploaded == "function" && props.uploaded.call(null, acceptedFiles);
}, [props.uploaded]);
const {
getRootProps,
getInputProps,
isFocused,
isDragAccept,
isDragReject,
acceptedFiles
} = useDropzone({
accept: {
'image/*': [],
'text/*': [],
'application/pdf': [],
},
multiple: false,
onDrop
});
const fileNames = acceptedFiles.map(file => file.name);
return (
<>
<DropZone {...getRootProps({isFocused, isDragAccept, isDragReject})}>
<input {...getInputProps()} />
<FileLabel fileNames={fileNames}/>
</DropZone>
</>
);
}

I'm using this component for several tools. It's easy and it works great. You can view a demo of it on my tools page Base64 Encode, where you can encode uploaded files to Base64 format.

Link: react-dropzone

React-Uploady

If you don't like to start from scratch and integrate a file upload in React with just a few lines of code, Uploady is for you.

As the repository states:

React-Uploady is a lightweight library - enabling you to build (client-side) file-upload features with just a few lines of code. Uploady provides the foundations needed to upload files from the browser - The rest is up to you.

It offers a easy to use Upload Button, a customizable preview component for files being uploaded, a dropzone component and a upload URL component to enter a URL that will be sent as an upload. The library is actively maintained and has around 900 Stars on Github.

Link: React-Uploady as Github

Further Information

Enjoyed this post?

My goal with this blog is to help people to get started with developing wonderful software while doing business and make a living from it.

Subscribe here to get my latest content by email.

I won't send you spam. You can unsubscribe at any time.

© 2024 Headystack. All rights reserved.
👋