React File Upload

Welcome to our React file upload tutorial. In this article, we’ll cover how to enable file uploads in your React app from scratch. If you want a simple plug & play solution, try our React Filepicker Component (you’ll need to create a free Filestack account to get your API key).

Understanding the File Upload Process in React:

Uploading files in a React app, such as images, documents, or any other file types, typically follows a structured approach:

  1. User File Selection: The journey begins with allowing the user to select a file. In React, this is commonly achieved by utilizing the <input> element with its type attribute set to “file”. This offers a user-friendly interface for file selection. When a file is chosen, it’s important to have an event handler in place. This event handler listens to any changes or interactions with the file input and updates the application’s state with the selected file’s information.
  2. Server Communication: Once the file information is captured and stored in the application’s state, the next pivotal step is sending it over to a server. This could be for processing, storage, or any other backend operation. Tools like axios or the native fetch API are frequently employed to handle this communication. They aid in making asynchronous HTTP requests to servers. It’s crucial to wrap the selected file in a FormData object, ensuring the data is properly formatted for transmission.
  3. Feedback & Response Handling: Upon initiating communication with the server, always anticipate two outcomes: success or failure. Implementing feedback mechanisms like success messages, error alerts, or even displaying the uploaded file helps improve user experience. It provides assurance that their action (file upload) was successful or gives clarity if something went amiss.
  4. Error Handling: The digital realm isn’t always predictable. Issues might arise during the upload process, be it network glitches, file format mismatches, or server-side errors. Being prepared with a robust error-handling mechanism not only aids in troubleshooting but also ensures users aren’t left in the dark. Informative error messages and alternative solutions can steer users in the right direction.
  5. External Libraries and Tools: While React provides a solid foundation, sometimes external libraries or tools can expedite the development process. Tools like axios simplify HTTP communications. Moreover, services like Filestack offer out-of-the-box file uploading solutions, saving development time.

By adhering to this structured approach, developers can ensure a smooth and efficient file upload process in their React applications, enhancing both functionality and user satisfaction.

Now let’s dive in to the nitty gritty details.

We’re starting with a freshly created react app with the default content removed.

import './App.css';

function App() {
  return (
    <div className="App">

    </div>
  );
}

export default App;

The first thing we’ll do is create a simple form where our user can choose what file to upload.

import './App.css';
function App() {
  return (
    <div className="App">
        <form>
          <h1>React File Upload</h1>
          <input type="file" />
          <button type="submit">Upload</button>
        </form>
    </div>
  );
}

export default App;

Next, we’ll create a state variable, add an onChange event handler to the input element, and create a handleChange function to keep track of what file our user chose to upload.

import './App.css';
import React, {useState} from react;

function App() {

  const [file, setFile] = useState()

  function handleChange(event) {
    setFile(event.target.files[0])
  }

  return (
    <div className="App">
        <form>
          <h1>React File Upload</h1>
          <input type="file" onChange={handleChange}/>
          <button type="submit">Upload</button>
        </form>
    </div>
  );
}

export default App;

Now that we know what file our user chose to upload, we’ll add axios for making http requests, an onSubmit event handler to the form, and a handleSubmit function to upload the file using a http POST request.

import './App.css';
import React, {useState} from 'react';
import axios from 'axios';

function App() {

  const [file, setFile] = useState()

  function handleChange(event) {
    setFile(event.target.files[0])
  }
  
  function handleSubmit(event) {
    event.preventDefault()
    const url = 'http://localhost:3000/uploadFile';
    const formData = new FormData();
    formData.append('file', file);
    formData.append('fileName', file.name);
    const config = {
      headers: {
        'content-type': 'multipart/form-data',
      },
    };
    axios.post(url, formData, config).then((response) => {
      console.log(response.data);
    });

  }

  return (
    <div className="App">
        <form onSubmit={handleSubmit}>
          <h1>React File Upload</h1>
          <input type="file" onChange={handleChange}/>
          <button type="submit">Upload</button>
        </form>
    </div>
  );
}

export default App;

This is the critical step when enabling file uploads using React. We’ve created a config object to specify a ‘content-type’ header for our http request. In order to upload files, the ‘content-type’ header must be set to ‘multipart/form-data’.

new FormData() creates a new empty formData object that we send as the payload in our POST request. Our POST request assumes there is an API endpoint on our backend server at http://localhost:3000/uploadFile.

Uploading Multiple Files

In many real-world applications, there’s a need for users to upload more than one file at a time. Let’s enhance our React app to support multiple file uploads.

import './App.css';
import React, { useState } from 'react';
import axios from 'axios';

function App() {
  const [files, setFiles] = useState([]);
  const [uploadedFiles, setUploadedFiles] = useState([]);

  function handleMultipleChange(event) {
    setFiles([...event.target.files]);
  }

  function handleMultipleSubmit(event) {
    event.preventDefault();
    const url = 'http://localhost:3000/uploadFiles';
    const formData = new FormData();
    files.forEach((file, index) => {
      formData.append(`file${index}`, file);
    });

    const config = {
      headers: {
        'content-type': 'multipart/form-data',
      },
    };

    axios.post(url, formData, config)
      .then((response) => {
        console.log(response.data);
        setUploadedFiles(response.data.files);
      })
      .catch((error) => {
        console.error("Error uploading files: ", error);
      });
  }

  return (
    <div className="App">
      <form onSubmit={handleMultipleSubmit}>
        <h1>React Multiple File Upload</h1>
        <input type="file" multiple onChange={handleMultipleChange} />
        <button type="submit">Upload</button>
      </form>
      {uploadedFiles.map((file, index) => (
        <img key={index} src={file} alt={`Uploaded content ${index}`} />
      ))}
    </div>
  );
}

export default App;
In this snippet, the input tag now has the multiple attribute, allowing users to select multiple files. We're iterating over the selected files, adding each one to our FormData object, and then displaying each uploaded file in the app.

File Upload Progress

Another enhancement is to provide users with feedback on the progress of their file upload.

import './App.css';
import React, { useState } from 'react';
import axios from 'axios';

function App() {
  const [file, setFile] = useState();
  const [uploadProgress, setUploadProgress] = useState(0);

  function handleChange(event) {
    setFile(event.target.files[0]);
  }

  function handleSubmit(event) {
    event.preventDefault();
    const url = 'http://localhost:3000/uploadFile';
    const formData = new FormData();
    formData.append('file', file);

    const config = {
      headers: {
        'content-type': 'multipart/form-data',
      },
      onUploadProgress: function(progressEvent) {
        const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        setUploadProgress(percentCompleted);
      }
    };

    axios.post(url, formData, config)
      .then((response) => {
        console.log(response.data);
      })
      .catch((error) => {
        console.error("Error uploading file: ", error);
      });
  }

  return (
    <div className="App">
      <form onSubmit={handleSubmit}>
        <h1>React File Upload with Progress</h1>
        <input type="file" onChange={handleChange} />
        <button type="submit">Upload</button>
        <progress value={uploadProgress} max="100"></progress>
      </form>
    </div>
  );
}

export default App;

Here, we’ve added an onUploadProgress function to our Axios config. This function updates our uploadProgress state variable with the current percentage of the upload. We display this percentage using the HTML5 progress element.

These enhancements not only provide a better user experience but also cater to more practical use cases in file uploading scenarios. Now let’s move forward to displaying the uploaded file.

Displaying the Uploaded File in the React App

After successfully uploading a file, it’s often beneficial for the user to get feedback and see the file they just uploaded. In this section, we’ll update the state with the uploaded file’s URL and display the file in our React app.

import './App.css';
import React, {useState} from 'react';
import axios from 'axios';

function App() {

  const [file, setFile] = useState()
  const [uploadedFileURL, setUploadedFileURL] = useState(null)

  function handleChange(event) {
    setFile(event.target.files[0])
  }

  function handleSubmit(event) {
    event.preventDefault()
    const url = 'http://localhost:3000/uploadFile';
    const formData = new FormData();
    formData.append('file', file);
    formData.append('fileName', file.name);
    const config = {
      headers: {
        'content-type': 'multipart/form-data',
      },
    };
    axios.post(url, formData, config).then((response) => {
      setUploadedFileURL(response.data.fileUrl);
    });
  }

  return (
    <div className="App">
        <form onSubmit={handleSubmit}>
          <h1>React File Upload</h1>
          <input type="file" onChange={handleChange}/>
          <button type="submit">Upload</button>
        </form>
        {uploadedFileURL && <img src={uploadedFileURL} alt="Uploaded content"/>}
    </div>
  );
}

export default App;

 

In this snippet, we added a new state variable uploadedFileURL which holds the URL of the uploaded file. After we get a successful response from the server, we update this state variable with the file’s URL which we then use to display the image in our application.

 

Handling React File Upload Errors

It’s good practice to handle potential errors that might occur during the file upload process. Let’s add some error handling to our handleSubmit function:

import './App.css';
import React, {useState} from 'react';
import axios from 'axios';

function App() {

  const [file, setFile] = useState();
  const [uploadedFile, setUploadedFile] = useState();
  const [error, setError] = useState();

  function handleChange(event) {
    setFile(event.target.files[0]);
  }
  
  function handleSubmit(event) {
    event.preventDefault();
    const url = 'http://localhost:3000/uploadFile';
    const formData = new FormData();
    formData.append('file', file);
    formData.append('fileName', file.name);
    const config = {
      headers: {
        'content-type': 'multipart/form-data',
      },
    };
    axios.post(url, formData, config)
      .then((response) => {
        console.log(response.data);
        setUploadedFile(response.data.file);
      })
      .catch((error) => {
        console.error("Error uploading file: ", error);
        setError(error);
      });
  }

  return (
    <div className="App">
        <form onSubmit={handleSubmit}>
          <h1>React File Upload</h1>
          <input type="file" onChange={handleChange}/>
          <button type="submit">Upload</button>
        </form>
        {uploadedFile && <img src={uploadedFile} alt="Uploaded content"/>}
        {error && <p>Error uploading file: {error.message}</p>}
    </div>
  );
}

export default App;

 

In the above code, we’ve added a catch block to our axios POST request that sets an error state variable in case of an error. We also render an error message to the screen if there was an error uploading the file.

By extending our application with these two sections, we’ve made it more user-friendly and robust. It’s now not only possible for users to upload files, but also to view the uploaded files and receive error messages in case something goes wrong during the upload process.

We’re done! If you want to learn about how to setup the backend server that would receive the POST request we made in this article, check out our articles on how to upload a file using NodeJS (or with Python if you prefer).

If you don’t want to go through the trouble of setting up a server, consider signing up for a free Filestack account.

FAQs

What is the purpose of the ‘handleChange’ function in the file upload process?

The ‘handleChange’ function is used to update the state with the file that a user chooses to upload. It sets the ‘file’ state variable to the file object from the event triggered by the file input.

Why do we use ‘multipart/form-data’ as the content-type in the config object?

When you want to upload a file, the ‘content-type’ should be set to ‘multipart/form-data’. This type is necessary when you are sending binary data in the body of the request, like the contents of a file.

How does the React app display the uploaded file?

Once the file is uploaded, the server returns the URL of the uploaded file which is then stored in the ‘uploadedFile’ state variable. This URL is used as the ‘src’ attribute of an img tag, allowing the uploaded image to be displayed in the React app.

How does the app handle errors that might occur during the file upload process?

The app handles errors by using a .catch() block with the axios POST request. If an error occurs during the file upload process, the catch block is executed, setting the ‘error’ state variable with the error message. This error message is then displayed on the screen.

What if I don’t want to set up a server for file upload functionality?

If you don’t want to set up a server, you could use services like Filestack. We provide SDKs & APIs for file uploading functionality that you can integrate into your app with just a few lines of code.

 

Read More →

Ready to get started?

Create an account now!