Sign in
Topics
Skip the trial-and-error. Use React and MUI to create a polished file upload component that just works.
This article provides a step-by-step guide to building a React MUI file upload feature. It explains how React and Material UI create a smooth, user-friendly interface. You'll also learn how to make your upload process clean, responsive, and easy to manage.
Many web apps require users to upload files like images or documents. To support this, developers often build upload features that connect users to the server with just a few clicks.
But what’s the best way to create one that looks clean and works smoothly?
This article walks through a simple approach using React and Material UI. The method is commonly known as React MUI file upload.
React helps you build responsive interfaces. Material UI adds pre-built components that follow Google’s design system. Together, they help you create a file upload feature that feels polished and easy to use.
Let’s take a closer look.
Before we dive into the code, let's set up our React project. Install Node.js and npm (Node Package Manager) on your machine if you haven't already. Once installed, you can create a new React project by running the following command in your terminal:
1npx create-react-app mui-file-upload
This command will create a new React project named "mui-file-upload". Navigate into the project directory using cd mui-file-upload and open the project in your preferred code editor.
Next, we need to install Material UI and Axios. Material UI will provide us with pre-built React components that follow Material Design principles, while Axios is a promise-based HTTP client for making requests to our server.
To install these libraries, run the following commands in your terminal:
1npm install @material-ui/core 2npm install axios
After installing, we can import the necessary components and functions from these libraries at the top of our React files.
Now, let's create a file upload button in React. We'll start by importing the necessary components from Material UI:
1import React from 'react'; 2import Button from '@material-ui/core/Button';
Next, we'll create a new function component named UploadButton:
1export default function UploadButton() { 2 return ( 3 <Button variant="contained" color="primary" component="span"> 4 Upload 5 </Button> 6 ); 7}
In this code, we're using the Button component from Material UI. We set the variant prop to "contained" to give the button a filled background and the color prop to "primary" to apply the primary color theme. The component prop is set to "span" to render the button as a span element. This button doesn't do anything yet, but we'll add functionality in the following sections.
Material UI provides two types of button components: Button and IconButton. The Button component is used for buttons with text labels, while the IconButton is used for icons.
The Button component can take a variety of props, including variant, color, and size, allowing for a high degree of customization. The IconButton on the other hand, is more minimalist and is typically used for navigation or UI control actions.
Here's an example of how to use the IconButton component:
1import IconButton from '@material-ui/core/IconButton'; 2import PhotoCamera from '@material-ui/icons/PhotoCamera'; 3 4export default function IconButtons() { 5 return ( 6 <IconButton color="primary" aria-label="upload picture" component="span"> 7 <PhotoCamera /> 8 </IconButton> 9 ); 10}
We use the IconButton component with the PhotoCamera icon in this code. The color prop is set to "primary", and the aria-label prop is used for accessibility.
Material UI allows for extensive customization of its components. For example, you can change the color, size, and variant of the Button component. You can also add icons and adjust the button's padding and margin.
Here's an example of a customized Button component:
1import Button from '@material-ui/core/Button'; 2import CloudUploadIcon from '@material-ui/icons/CloudUpload'; 3 4export default function UploadButton() { 5 return ( 6 <Button 7 variant="contained" 8 color="primary" 9 startIcon={<CloudUploadIcon />} 10 > 11 Upload 12 </Button> 13 ); 14}
In this code, we're using the startIcon prop to add an icon to the start of the button. The CloudUploadIcon is imported from Material UI's icon library.
Next, let's create a Material UI file upload form. This form will contain an input field for selecting files and a button for initiating the upload.
First, we'll import the necessary components:
1import React from 'react'; 2import Button from '@material-ui/core/Button'; 3import TextField from '@material-ui/core/TextField'; 4
Then, we'll create a new function component named UploadForm:
1export default function UploadForm() { 2 return ( 3 <form> 4 <TextField type="file" /> 5 <Button variant="contained" color="primary" component="span"> 6 Upload 7 </Button> 8 </form> 9 ); 10}
In this code, we're using the TextField component for the file input field and the Button component for the upload button. The type prop of the TextField component is set to "file" to allow for file selection.
To handle file upload, we'll use Axios, a promise-based HTTP client. Axios allows us to make HTTP requests from the browser to the server, and it supports the Promise API.
First, let's import Axios:
1import axios from 'axios'; 2
Next, we'll create a function to handle the file upload:
1const handleFileUpload = (event) => { 2 const file = event.target.files[0]; 3 const formData = new FormData(); 4 formData.append('file', file); 5 6 axios.post('/upload', formData) 7 .then(response => { 8 console.log(response); 9 }) 10 .catch(error => { 11 console.error(error); 12 }); 13}; 14
In this function, we're creating a new FormData object and appending the selected file to it. Then, we're making a POST request to the '/upload' endpoint with the form data. The server should handle the file upload and return a response.
After a file is uploaded, it's a good practice to provide feedback to the user. One common way to do this is by displaying a progress bar. Material UI provides a LinearProgress component for this purpose.
First, let's import the LinearProgress component:
1import LinearProgress from '@material-ui/core/LinearProgress'; 2
Next, we'll create a state variable to keep track of the upload progress:
1const [uploadProgress, setUploadProgress] = React.useState(0); 2
Then, we'll update the handleFileUpload function to update the uploadProgress state variable:
1axios.post('/upload', formData, { 2 onUploadProgress: (progressEvent) => { 3 const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); 4 setUploadProgress(percentCompleted); 5 } 6}) 7
Finally, we'll display the LinearProgress component in our render method:
1<LinearProgress variant="determinate" value={uploadProgress} /> 2
In this code, the variant prop is set to "determinate" to indicate that the progress value is known, and the value prop is set to the uploadProgress state variable.
During the file upload process, various errors may occur. For instance, the file might be too large, the file type might not be supported, or the server might be down.
Handling these errors gracefully and providing informative feedback to the user is essential.
Let's update our handleFileUpload function to handle errors:
1axios.post('/upload', formData, { 2 onUploadProgress: (progressEvent) => { 3 const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); 4 setUploadProgress(percentCompleted); 5 } 6}) 7.then(response => { 8 console.log(response); 9}) 10.catch(error => { 11 console.error(error); 12 // handle error here 13}); 14
In the catch block, we're logging the error to the console. In a real application, you might want to display an error message to the user or take other appropriate actions.
Material UI provides a powerful styling solution called makeStyles that allows you to write CSS in JavaScript. With makeStyles, you can create a hook that generates a class name map, which you can then use to style your components.
Here's an example of how to use makeStyles to style our file upload component:
1import { makeStyles } from '@material-ui/core/styles'; 2 3const useStyles = makeStyles((theme) => ({ 4 root: { 5 '& > *': { 6 margin: theme.spacing(1), 7 }, 8 }, 9 input: { 10 display: 'none', 11 }, 12})); 13 14export default function UploadButtons() { 15 const classes = useStyles(); 16 17 return ( 18 <div className={classes.root}> 19 <input 20 accept="image/*" 21 className={classes.input} 22 id="contained-button-file" 23 multiple 24 type="file" 25 /> 26 <label htmlFor="contained-button-file"> 27 <Button variant="contained" color="primary" component="span"> 28 Upload 29 </Button> 30 </label> 31 32 ); 33} 34
In this code, we're using the makeStyles function to create a useStyles hook. We then call this hook inside our UploadButtons component to generate the class names. Finally, we apply these class names to our elements using the className prop.
A higher-order component (HOC) in React is a function that takes one component and returns another with additional properties or behavior. HOCs are useful for abstracting and reusing component behavior.
We can create a HOC that adds file upload functionality to any component. Here's an example:
1function withFileUpload(WrappedComponent) { 2 return class extends React.Component { 3 handleFileUpload = (event) => { 4 // handle file upload here 5 }; 6 7 render() { 8 return ( 9 <WrappedComponent 10 onFileUpload={this.handleFileUpload} 11 {...this.props} 12 /> 13 ); 14 } 15 }; 16}
In this code, withFileUpload is a HOC that adds a handleFileUpload method to the wrapped component. The wrapped component can then use this method to handle file upload.
React’s component-based structure makes file upload features modular and easy to manage across your app. Material UI offers pre-built components that simplify layout and styling.
Using Axios for file handling keeps your code clean and promise-based, making upload and download actions easier to control. When you move file upload logic into a reusable component, your code stays clean and easier to maintain.
Combining React, Material UI, and Axios allows you to build a clear and flexible React MUI file upload feature with smooth functionality and design.