React file upload: the easy way, with NodeJS!
Tags: react, javascript
Published: 2019-04-19 09:59:20

---
title: React file upload: the easy way, with NodeJS!
published: true
tags: reactjs,javascript
canonical_url: https://medium.com/quick-code/react-file-upload-the-easy-way-with-nodejs-e94c5e81fb8
---

### How to start upload file with React and Nodejs

#### In this tutorial, we go through building a full-stack file upload solution with express and React.js

![](https://cdn-images-1.medium.com/max/940/1*1DOMi_VcKhBSHU5GmBJFcw.png)

we gonna create a basic form for handle file upload with React and server-side with Nodejs this tutorial is so simple you can follow along with 5 minutes it should be done if you think TLDR; just check [finish code here](https://github.com/krissnawat/react-file-upload/tree/master) and live demo will host on Codesandbox

[upload-front - CodeSandbox](https://codesandbox.io/s/github/krissnawat/react-file-upload/tree/master/?fontsize=14)

### Setting up the Node server

If you’re following for the full tutorial, the complete server code is available. Go to server directory, open terminal and execute npm install and node index , this will run the server. Then continue from the React section.

1. Create a directory _upload-server_, open terminal in this directory and run npm init. Accept all defaults.
2. In the same terminal, install upload and cors package will commandnpm express express-fileupload cors

**start on index.js is the entry file.**

Here’s the Initial code for the server.

```
const PORT = 5000;
const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.get('/', (req, res) => {
  return res.status(200).send("It's working");
});

app.listen(PORT, () => {
  console.log('Server Running sucessfully.');
});
```

Now, go to your browser at _localhost:5000_ and you should see **It’s working.**

### Upload Endpoint

Let’s define /upload at the upload endpoint. It needs the middleware fileUpload, set this thing first.

```
app.use(
  fileUpload({
    useTempFiles: true,
    safeFileNames: true,
    preserveExtension: true,
    tempFileDir: `${__dirname}/public/files/temp`
  })
);
```

It accepts many options, which you can [find here](https://www.npmjs.com/package/express-fileupload).

The temporary directory is used to store files temporarily instead of using computer memory(RAM). This is very useful in uploading large files.

Safe File removes non-alphanumeric characters from except dash and underscores from the filename. You can also define regex instead of true for custom names.

_preserveExtension_ preserves the extension of the file name, this is false by default. It also supports customizations, [refer to the documentation for options](https://www.npmjs.com/package/express-fileupload).

```
app.post('/upload', (req, res, next) => {
  let uploadFile = req.files.file;
  const name = uploadFile.name;
  const md5 = uploadFile.md5();
  const saveAs = `${md5}_${name}`;
  uploadFile.mv(`${__dirname}/public/files/${saveAs}`, function(err) {
    if (err) {
      return res.status(500).send(err);
    }
    return res.status(200).json({ status: 'uploaded', name, saveAs });
  });
});
```

The **file** in _req.files.file_ refers to **_file_** as a name in the input field. That is **NAME** in both <input name=" **NAME**" type="file" /> and req.file. **NAME** should be the same.

To prevent overwriting the files, I used an md5 function from the uploadFile package and saved the file as **_md5\_filename.ext_**.

You can test the API using Postman as well.

![](https://cdn-images-1.medium.com/max/1024/0*D046OXLOhSAHjD17.png)

Send a post request to /upload with a file. It should successfully go through.

### React Front End

A server is waiting for files, you need an interface to allow users to input the files. Let’s create an interface using React.

Set up the react application with **create-react-app.**

npx create-react-app upload-front

Put a form with input type file in App.js. It should look like the following:

```
import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      <form className="App">
        <input type="file" name="file" />
        <button>Upload</button>
      </form>
    );
  }
}

export default App;
```

The button won’t upload by itself. Define a few methods and states to achieve functionality.

**State:** selectedFile

**Methods:** handleFileChange, and handleUpload

Add on change listener as handleFileChange on input. You can attach _handleFileUpload_ as a form submit an action or as click listener on a button. We’ll use the state to hold file so it doesn’t matter.

Here’s how it looks.

```
import React, { Component } from 'react';

class App extends Component {
  handleFileChange = () => {
    // define file change
  };
  handleUpload = () => {
    // define upload
  };
  render() {
    return (
      <form className="App" onSubmit={handleUpload}>
        <input type="file" name="file" onChange={handleFileChange} />
        <button onClick={handleUpload}>Upload</button>
      </form>
    );
  }
}

export default App;
```

**Do not keep _handleUpload_ on both _onClick_ and _onSubmit._** One is enough.

Handling the file change, we keep track of file and an upload button click.

### Handling file change

```
handleFileChange = (event) => {
    this.setState({
      selectedFile: event.target.files[0]
    });
  };
```

The file input is an event for the click and files are stored in an array (even in single file upload).

### Handling the Upload

To handle the uploading, we’ll use Axios to send ajax request. Install and import Axios.

npm i axios

import axios from 'axios

```
handleUpload = (event) => {
    event.preventDefault();
    const data = new FormData();
    data.append('file', this.state.selectedFile, this.state.selectedFile.name);
    axios.post(endpoint, data).then((res) => {
      console.log(res.statusText);
    });
  };
```

A file is sent as FormData. It just logs the response text after completing the AJAX request.

While it looks too ugly, uploading should start working now.

### Loading State

While the file is uploaded successfully, the user doesn’t see the response. Showing the response after the completion is quite simple. Let’s show the progress instead.

**axios** can take some parameters with the post method. One of them is _ProgressEvent_.

```
{
        onUploadProgress: ProgressEvent => {
          this.setState({
            loaded: (ProgressEvent.loaded / ProgressEvent.total\*100),
          }
```

This sets a loaded state to whatever percentage is completed.

We also need to display it, so wrap it in a paragraph outside the form. This will crash the application as JSX can’t return multiple parts. Wrap the return in React Fragment.

Here’s the code:

```
return (
      <React.Fragment>
        <form className="App" onSubmit={this.handleUpload}>
          <input
            className="inputfile"
            id="file"
            type="file"
            name="file"
            onChange={this.handleFileChange}
          />
          <label htmlFor="file">Choose a file</label>
          <button onClick={this.handleUpload}>Upload</button>
        </form>
        <p>{Math.round(this.state.loaded)}%</p>
      </React.Fragment>
    );
```

I’ve given it a label, that we’ll play with soon. Math.round is to avoid decimal values.

But this will throw error initially as the state loaded doesn’t even exist.

Initialize the state on a component mount with:

```
state = { selectedFile: null, loaded: null };
```

There’s no need of defining a constructor for a state, if you’ve been doing that way.

Here’s how **handleUpload** is now…

```
handleUpload = (event) => {
    // define upload
    event.preventDefault();
    const data = new FormData();
    data.append('file', this.state.selectedFile, this.state.selectedFile.name);
    axios
      .post(endpoint, data, {
        onUploadProgress: (ProgressEvent) => {
          this.setState({
            loaded: (ProgressEvent.loaded / ProgressEvent.total) \* 100
          });
        }
      })
      .then((res) => {
        console.log(res.statusText);
      });
  };
```

### Unuglifying things…

This is how ugly it is right now…

![](https://cdn-images-1.medium.com/max/574/0*hpPdsqxXYibgv501.png)

I grabbed CSS from [here](https://tympanus.net/Tutorials/CustomFileInputs/) and moved to index.css.

I also added states _uploading_, _message,_ and _defaultMessage_. If it is currently uploading, the **loaded** state will show the % completed. Otherwise, the **message** will be shown.

The JSX returned has also changed slightly, the react part and methods remain the same. This comes from [that same HTML file](https://tympanus.net/Tutorials/CustomFileInputs/).

```
return (
      <form className="box" onSubmit={this.handleUpload}>
        <input
          type="file"
          name="file-5[]"
          id="file-5"
          className="inputfile inputfile-4"
          onChange={this.handleFileChange}
        />
        <label htmlFor="file-5">
          <figure>
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="20"
              height="17"
              viewBox="0 0 20 17"
            >
              <path d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z" />
            </svg>
          </figure>
          <span>
            {this.state.uploading
              ? this.state.loaded + '%'
              : this.state.message}
          </span>
        </label>
        <button className="submit" onClick={this.handleUpload}>
          Upload
        </button>
      </form>
    );
```

![](https://cdn-images-1.medium.com/max/1024/0*2XGPJRJs9DL2y4sg.png)

After adding some CSS for the button as well, it looks like:

![](https://cdn-images-1.medium.com/max/1024/0*uLJSbOKpbuNlj1gv.png)

If you click on the upload button without choosing a file, it’ll crash.

![](https://cdn-images-1.medium.com/max/907/0*b_G7mZgOyUG5CLzh.png)

This is because the selected file is null, and null doesn’t have the property of name.

To fix this, return from the _handleUpload_ method if the _selectedFile_ is _null._ Also, set the message to _upload first_ so the user gets some feedback on what to do.

```
handleUpload = (event) => {
    event.preventDefault();
    if (this.state.uploading) return;
    if (!this.state.selectedFile) {
      this.setState({ message: 'Select a file first' });
      return;
    }
    this.setState({ uploading: true });
    const data = new FormData();
    data.append('file', this.state.selectedFile, this.state.selectedFile.name);
    axios
      .post(endpoint, data, {
        onUploadProgress: (ProgressEvent) => {
          this.setState({
            loaded: Math.round(
              (ProgressEvent.loaded / ProgressEvent.total) \* 100
            )
          });
        }
      })
      .then((res) => {
        this.setState({
          selectedFile: null,
          message: 'Uploaded successfully',
          uploading: false
        });
        console.log(res.statusText);
      });
  };
```

Also, I’ve rounded the _loaded_ state itself instead of rounding in the output. If you click upload without selecting the file, the message will change to _select a file first_.

![](https://cdn-images-1.medium.com/max/434/0*ZZvPMO3J2UH81CtB.png)

You can upload another file before the previous one has finished. We can deal with this by using the unloading state. Just return from the handle upload without uploading if the uploading is true.

```
if (this.state.uploading) return;
    if (!this.state.selectedFile) {
      this.setState({ message: 'Select a file first' });
      return;
    }
    this.setState({ uploading: true });
```

After successfully uploading, it changes the **message** to **_Uploaded Successfully_** while **uploading** state changes to _false._

**handleFileChange** is modified to show the file name that’s going to upload.

```
handleFileChange = (event) => {
    this.setState({
      selectedFile: event.target.files[0],
      loaded: 0,
      message: event.target.files[0]
        ? event.target.files[0].name
        : this.state.defaultmessage
    });
  };
```

### Error Handling

If you shut down the server, this application will still try to upload the file and loaded will change to NaN (Not a Number). Since _uploading_ was set _true_, you can’t upload even the server comes back as _handleUpload_ is blocked by this.

Add a catch to the post request..

```
.catch((err) => {
        this.setState({
          uploading: false,
          message: 'Failed to upload'
        });
      });
```

Awesome, it uploads a file while displaying the message on error and success.

![](https://cdn-images-1.medium.com/max/1024/0*xNmrMnZ8D4PG9if9.gif)

#### Conclusion

your learn how to basic upload with Reactjs and Nodejs in an easy way to continue next step you can implement validation on client and server or show preview before upload

### Featured React JS Courses

#### [React 16 — The Complete Guide (incl. React Router 4 & Redux)](https://click.linksynergy.com/link?id=qt/jYwyHv8A&offerid=507388.1362070&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Freact-the-complete-guide-incl-redux%2F)

4.7/5 Stars **||** 33.5 Hours of Video **||** 61,597 Students

Learn React or dive deeper into it. Learn the theory, solve assignments, practice in demo projects and build one big application which is improved throughout the course: The Burger Builder! [**Learn More.**](http://thebestdevlist.link/react-the-complete-16-guide-incl-redux)

[React 16: The Complete Course (incl. React Router 4 & Redux)](https://click.linksynergy.com/link?id=qt/jYwyHv8A&offerid=507388.1362070&type=2&murl=https%3A%2F%2Fwww.udemy.com%2Freact-the-complete-guide-incl-redux%2F)

### Disclaimer

_This post contains affiliate links to products. We may receive a commission for purchases made through these links._

* * *