How to Upload a File Using React
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 sizeconst file = event.target.files[0];console.log(file);setFile(file);// to access the files content you need the FileReader objectconst 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:
- Add a file input element:
<input type="file" />
- Create a
fileChangeHandler
method and read file information fromevent.target.files[0]
- Store the file in component’s state and add file information to html
- 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 serverfetch('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 serveraxios.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 sizeconst file = event.target.files[0];// set file statesetFile(file);// create FormData object to prepare file for uploadconst formData = new FormData();formData.append("file", file);// Use Axios to upload the file to any serveraxios.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 textsetProgress(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> byteslength selected.</p><divstyle={{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.