Creating a file upload component is a common task in web development.
Unfortunately it can be quite intimidating.
In this tutorial you will learn how to create a working file upload component with react from scratch using no dependencies other than react itself.
You will learn how to upload files and display the upload progress using react.
Here is what the result will look like:
Also, we will start by building a server we can upload files to using nodejs and express. We will walk through the full full-stack process to make sure you end up with a working system.
Ready?
Let’s get started!
Creating the express server to upload to
Before we can have a react app upload anything, we need to have a server we can upload to, right?
In this tutorial, we are going to implement a file upload server using node.js and express, that allows file to be uploaded in in the multipart format.
Setting up a new project
To set up a new project, create a new project directory containing two folders app and server.
Next, open the command line pointing at the server directory and initialize a new npm-project using the
npm init
command. It will ask you for some information. You can pass it in or just hit enter.
For the server, we will also need two JavaScript files. Create the following files in the project directory:
server.js upload.js
Go ahead and create them inside of the server directory.
External Dependencies
Our server will have three external dependencies.
The first one is obviously express. Express is a framework, that makes creating API very easy. To install express, you can use this command inside of the server directory:
npm install express
Because we want to access the API from a react application that is probably served from another origin, the server needs to allow cross-origin requests. Therefore we are going to use a simple module called CORS. To install it, type:
npm install cors
Also, express itself is not very good at understanding forms. Because we will be uploading our files in the multipart/form-data format, we need to be able to parse this format. The library “formidable” does this and is quite easy to use. Install formidable using this command:
npm install formidable
That’s all we need. Now it is time to start building our file upload server…
Setting up a basic express server
First, we need to create a basic express server in the server.js file. This part looks always the same and consists of only 3 lines.
Good job! You have build your first express server (in case you haven’t done so before).
Although it does nothing useful, you can fire it up using the
node server.js
command. As this is using a relative path, make sure you have opened the terminal at the server directory. Otherwise, you need to adjust the relative path (for example server/server.js).
Enabling CORS
To be able to access our API from a react application with a different origin, we need to enable cors. To do so, we first need to require CORS.
Next, we configure it to allow any domain by creating an option-object.
Finally, we tell express to use the cors-middleware with our configuration.
Registering the upload route
Afterward, we need to configure a route for our file upload.
For that, we require our upload.js file and register a route with the HTTP-post method.
Now we are done with the server.js file. It should look like this by now:
Implementing the upload route
Let’s start implementing the upload functionality. We will place it into the upload.js file.
First, we need to require a class called IncomingForm from the “formidable” library.
After that, we need to export the callback function, we are using in our server.js to register the route. This function will be called, every time somebody hits the ‘/upload’ URL.
This callback gives us a request-object (req), that stores information about the request that hit the route.
We also get a response-object (res). We can use this object, to send back a response.
Inside of that method, we create a new form.
We then register callbacks on that form. The first callback is called for every file in the form:
The uploaded files are stored in a temporary directory somewhere on your machine. To do something with them, you can copy them from there using the node.js file-system API.
The second callback is called when the form is completely parsed. In this case, we want to send back a success status code.
We then trigger the parsing of the form using:
That’s all we will do for the upload functionality. It is not production ready, but it will help us to test our upload-component of the react application we actually want to build.
Here is the complete upload.js file:
Creating a new react project
Now it is time to start implementing the actual file upload component.
Before we can do that, we need to create a new react project inside of our project directory. Let’s create one by using the create-react-app CLI.
create-react-app app
We call the project app, because we want it to go into the app folder we have created before.
Adjusting the create-react-template
For our component to be displayed, we need to adjust the code, the CLI has generated for us, a little bit.
Of course, we need to place the upload component we are going to create on the DOM. In this case, it is wrapped by two divs that are responsible for the card-look you can see in the final result.
To make it look nice, we add some stylesheets:
Creating the upload component
For this to work properly, we need to create the upload component we have used inside the app component.
To do that, create a new folder inside the apps’ src-directory called “upload”.
Inside of that directory, create two files. Upload.js and Upload.css.
Next, create an empty component inside of the Upload.js file:
While we are at it, we can also add the overall layout of the component.
The file upload component will be split into two parts. On the left side will be a file dropzone to add new files, while on the right side there will be the list of files to be uploaded and their respective upload progress.
Finally, on the bottom right there will be an actions-section containing the upload button.
To accomplish this, the HTML layout looks like this:
To arrange the divs in the way described above, we are using flexbox in our stylesheet:
How to build a file dropzone component
Next, we need to create the file dropzone component, which will enable the user by selecting them in a file dialog or simply dropping them onto the component.
You can learn how to create a file dropzone component with react in this article.
Just as described in the article, our dropzone will be placed inside a directory called “dropzone” under the src-directory. So if you created your dropzone component following that article, you can just copy and paste that folder into this project.
Afterward, place the dropzone component into the upload component like so:
As you can see, the dropzone component takes two properties. The first one, “onFilesAdded” expects a callaback that gets notified when new files were added by the user.
With the disabled property, we can control whether the dropzone is accepting input.
Adjusting the components’ state
To keep track of the upload components’ state, we want to introduce some state variables.
First of all, we need an array containing all files to be uploaded.
Other than that, we need to keep track if the component is currently busy uploading files or if the upload succeeded.
Finally, we want to keep track of the upload progress of all files. For that we use a JavaScript object to create a map.
Because we are changing the constructor anyway, let’s add some bindings for future methods, as well.
Handling new files
To handle new files added via the dropzone, we need to implement a method called “onFilesAdded”.
In this mehtod, we simply add the new files to the already added files by setting the state accordingly.
Showing the list of files
Next, we are going to display the files that should be uploaded in a nice list.
Doing so is quite simple. All we need to do is map each element in our files state variable to a new row of our list.
Again, there is some CSS involved to make everything look nice:
Building a progress bar with react
Now that we know we want to display the upload progress to the user, we should also have a nice progress bar component right?
As always go ahead and create a new component called Progress by creating JavaScript and CSS files in a directory called progress.
The progress component is quite simple. It takes one property, which is the percentage to display in a range between 0 and 100.
For the progress bar effect we are using a nested div element:
To make these divs look like progress bars, use CSS similar to this:
Adding the progress bar to the file list
Ok, let’s add the progress bar to each file in the list. Because we need to check for multiple conditions, the rendering is extracted into a method called “renderProgress”.
Inside of the “renderProgress” method, we are extracting the upload progress from the state of the component, by using the name of the file.
Then, we check whether the file is currently being uploaded or if the upload succeed. Only if one of these conditions is true, we want to render a progress bar.
Afterwards, we pass the progress to the progress component. Additionally you can add an icon that is displayed on the right side of the progress bar, when the upload completed successfully. The visibility of this icon is controlled by its opacity, to avoid an rearrangement of the progress bar.
You can add any icon you want. Just place it in the apps public folder and use the filename in the src-attribute of the image above.
The icon used in this example can be found at the material design icons website.
Also, we need to add some CSS classes:
Rendering Actions to upload the files
There is still one section of the user interface, we haven’t implemented yet. The action-section containing the button to trigger the upload.
Because the rendering of the button also depends on some conditions, the rendering was extracted to another mehtod called “renderActions”.
This is because we want the button to have different text and behave in a different way, when the upload completed. In that case, we want the button to clear the file list and reset the components state.
Otherwise, the button should trigger the upload of the files.
Also, the button should be disabled when the file list is empty or the component is currently uploading files:
For the buttons to look nice, we need to include some CSS:
Uploading files
Let’s implement the “uploadFiles” method we referenced above.
Other than the other methods, this method is async so we can wait until all files are uploaded successfully.
So what is happening here?
First of all, we clear any uploadProgress that may be left from a previous upload. Also, we set the uploading-flag to true.
Next, we create an array to hold a promise for each file upload. This array is filled by calling the “sendRequest” method (which we will implement next) for each file, triggering the upload of the file.
Then, we wait until every upload is completed by awaiting .all promises.
Finally, we set the successfullUploaded-flag to true and the uploading-flag to false.
Uploading files to the server using XMLHttpRequest
To upload the files to the server, we implement the “sendRequest” method.
All this method does for now, is creating a new Promise. Inside of that promise, we create a new XMLHttpRequest and send that to the url of our file upload server using a post request and the file to upload wrapped into a FromData object.
Reporting file upload progress
The last piece that is missing is to fill our uploadProgress state variable with some actual progress data.
To get this data, we can add some event listener to the XMLHttpRequest object.
Namely there are the “progress”, “load” and “error” events we need to listen to.
In each case, the state is then updated containing the new progress data.
Conclusion
In this tutorial, we learned how to set up a node.js server-application from scratch and created a very basic file-upload route.
We also created a react file-upload component and styled it to look good.
You can find the full source code at the corresponding GitHub repository.
I hope you enjoyed this post.
If you did please hit the share buttons below and help other people building their own file-upload-components, as well.
Thanks for reading!