Building a Multiple File Uploader with React and Material-UI

Building a Multiple File Uploader with React and Material-UI

File uploads are a fundamental aspect of modern web applications. Whether you're designing a social media platform, a document management system, or a simple image gallery, providing users with the ability to upload multiple files efficiently is essential. In this blog post, we'll take you on a journey through the process of creating a multiple-file uploader using React and Material-UI. We'll also incorporate some additional animations to enhance the user experience.

Introduction

The primary goal of this blog post is to guide you through the creation of a multiple file uploader using React and Material-UI. This uploader will not only be functional but will also offer an aesthetically pleasing and interactive user interface. We'll incorporate several packages and libraries to make this possible, each playing a significant role in the overall design and functionality.

Required Packages

  • lodash: We'll use this package to check if a variable is a string, which is useful for validating file data.
  • @iconify/react: Icons add a layer of visual appeal to our uploader. We'll utilize icons like file and close icons from this package to make our uploader more user-friendly.
  • react-dropzone: This library simplifies handling file uploads and creating a user-friendly drop zone within our React application.
  • framer-motion: To provide a delightful and interactive user experience, we'll employ Framer Motion to create smooth animations in our uploader.
  • @material-ui/core: Material-UI is a popular library for designing modern and visually appealing user interfaces. We'll use its components to structure and style our uploader.

The UploadMultiFile Function

The UploadMultiFile component provides a comprehensive solution for managing multiple file uploads. It's the heart of our uploader and offers several essential features. It ensures that your users can effortlessly upload files, view previews, and remove unwanted files with ease. The addition of animations through Framer Motion adds that extra layer of interactivity that modern web applications demand.

Props

  • error: This prop allows us to indicate if any errors have occurred during the file upload process, ensuring a clear and transparent user experience.
  • showPreview: By toggling this prop, we can control whether file previews are displayed, providing users with a choice between a minimalist or detailed view of uploaded files.
  • files: This prop is an array that stores the uploaded files.
  • onRemove: A function that enables the removal of specific files, granting users the flexibility to manage their uploads.
  • onRemoveAll: This function is used to remove all uploaded files in a single click, streamlining the cleanup process.
  • sx: We've provided this prop for custom styling, allowing you to tailor the appearance of the component to match your application's design.

Key Features

  • File Drop Zone: Our component provides an eye-catching drop zone area where users can drag and drop files or simply click to select them. This intuitive design ensures that even first-time users can easily upload files.
  • Error Handling: We've implemented robust error handling to address situations where files may be rejected, such as when attempting to upload invalid file types. In such cases, we display error messages alongside a list of rejected files, aiding users in understanding and resolving issues.

import { isString } from 'lodash';
import PropTypes from 'prop-types';
import { Icon } from '@iconify/react';
import { useDropzone } from 'react-dropzone';
import fileFill from '@iconify/icons-eva/file-fill';
import closeFill from '@iconify/icons-eva/close-fill';
import { motion, AnimatePresence } from 'framer-motion';
import { alpha, styled } from '@material-ui/core/styles';
import {
  Box,
  List,
  Stack,
  Paper,
  Button,
  ListItem,
  Typography,
  ListItemIcon,
  ListItemText,
  ListItemSecondaryAction
} from '@material-ui/core';
import { fData } from '../../utils/formatNumber';
import { MIconButton } from '../@material-extend';
import { varFadeInRight } from '../animate';
import { UploadIllustration } from '../../assets';
  • Uploaded Files List: Our uploader neatly displays the uploaded files. If showPreview is enabled, users are presented with a visual preview of the file, complete with a convenient remove button. If showPreview is disabled, the component provides a simplified list view, showcasing the file name, size, and a remove button for each file.
// Uploaded Files Display
<List disablePadding sx={{ ...(hasFile && { my: 3 }) }}>
  <AnimatePresence>
    {files.map((file) => {
      const { name, size, preview } = file;
      const key = isString(file) ? file : name;
      if (showPreview) {
        return (
          <ListItem
            key={key}
            component={motion.div}
            {...varFadeInRight}
            sx={{
              p: 0,
              m: 0.5,
              width: 80,
              height: 80,
              borderRadius: 1.5,
              overflow: 'hidden',
              position: 'relative',
              display: 'inline-flex'
            }}
          >
            <Paper
              variant="outlined"
              component="img"
              src={isString(file) ? file : preview}
              sx={{ width: '100%', height: '100%', objectFit: 'cover', position: 'absolute' }}
            />
            <Box sx={{ top: 6, right: 6, position: 'absolute' }}>
              <MIconButton
                size="small"
                onClick={() => onRemove(file)}
                sx={{
                  p: '2px',
                  color: 'common.white',
                  bgcolor: (theme) => alpha(theme.palette.grey[900], 0.72),
                  '&:hover': {
                    bgcolor: (theme) => alpha(theme.palette.grey[900], 0.48)
                  }
                }}
              >
                <Icon icon={closeFill} />
              </MIconButton>
            </Box>
          </ListItem>
        );
      }
      return (
        <ListItem
          key={key}
          component={motion.div}
          {...varFadeInRight}
          sx={{
            my: 1,
            py: 0.75,
            px: 2,
            borderRadius: 1,
            border: (theme) => `solid 1px ${theme.palette.divider}`,
            bgcolor: 'background.paper'
          }}
        >
          <ListItemIcon>
            <Icon icon={fileFill} width={28} height={28} />
          </ListItemIcon>
          <ListItemText
            primary={isString(file) ? file : name}
            secondary={isString(file) ? '' : fData(size)}
            primaryTypographyProps={{ variant: 'subtitle2' }}
            secondaryTypographyProps={{ variant: 'caption' }}
          />
          <ListItemSecondaryAction>
            <MIconButton edge="end" size="small" onClick={() => onRemove(file)}>
              <Icon icon={closeFill} />
            </MIconButton>
          </ListItemSecondaryAction>
        </ListItem>
      );
    })}
  </AnimatePresence>
</List>
* 

Action Buttons: When users upload files, the component presents two action buttons: "Remove all" and "Upload files." These buttons offer users the option to manage their uploads efficiently.

// Action Buttons
{hasFile && (
  <Stack direction="row" justifyContent="flex-end">
    <Button onClick={onRemoveAll} sx={{ mr: 1.5 }}>
      Remove all
    </Button>
    <Button variant="contained">Upload files</Button>
  </Stack>
)}

Animation: To create a polished and engaging user experience, we've integrated Framer Motion. This library enables smooth transitions and visually appealing animations when files are added or removed from the uploader.

// Animation
<AnimatePresence>
  {files.map((file) => {
    const { name, size, preview } = file;
    const key = isString(file) ? file : name;
    if (showPreview) {
      // Animation for file preview
      return (
        <ListItem
          key={key}
          component={motion.div}
          {...varFadeInRight}
          sx={{
            p: 0,
            m: 0.5,
            width: 80,
            height: 80,
            borderRadius: 1.5,
            overflow: 'hidden',
            position: 'relative',
            display: 'inline-flex'
          }}
        >
          {/* ... */}
        </ListItem>
      );
    }
    // Animation for file list
    return (
      <ListItem
        key={key}
        component={motion.div}
        {...varFadeInRight}
        sx={{
          // ...
        }}
      >
        {/* ... */}
      </ListItem>
    );
  })}
</AnimatePresence>

Drop Zone Style: In this section, we'll explore the styling of the drop zone area. A well-designed drop zone can make a significant difference in how users perceive the file-uploading process. We'll explain how to create an interactive and visually appealing drop zone.

  • Show Rejection Items: File rejections are a common part of file uploads. In this section, we'll walk you through how our component handles rejected files. We display these files along with error messages, ensuring that users have clarity on any issues that may arise.
  • File Uploading UI: In this part, we'll discuss the code responsible for displaying the drag-and-drop area and handling file rejections. This is the core of the uploader's user interface, and we'll explain how it ensures a user-friendly experience.
  • Uploaded Files Display: We'll provide insights into the code for displaying uploaded files. We'll explain how the component handles the presentation of files, whether as previews or in a list format. Additionally, we'll cover the process of removing files.
  • Action Buttons: Finally, we'll explore the code for action buttons, including "Remove all" and "Upload files." These buttons are pivotal to users' interactions with the uploader, and we'll ensure you understand how they function.
  • Drop Zone Style: In this section, we'll explore the styling of the drop zone area. A well-designed drop zone can make a significant difference in how users perceive the file-uploading process. We'll explain how to create an interactive and visually appealing drop zone.
  • Show Rejection Items: File rejections are a common part of file uploads. In this section, we'll walk you through how our component handles rejected files. We display these files along with error messages, ensuring that users have clarity on any issues that may arise.
  • File Uploading UI: In this part, we'll discuss the code responsible for displaying the drag-and-drop area and handling file rejections. This is the core of the uploader's user interface, and we'll explain how it ensures a user-friendly experience.
  • Uploaded Files Display: We'll provide insights into the code for displaying uploaded files. We'll explain how the component handles the presentation of files, whether as previews or in a list format. Additionally, we'll cover the process of removing files.
  • Action Buttons: Finally, we'll explore the code for action buttons, including "Remove all" and "Upload files." These buttons are pivotal to users' interactions with the uploader, and we'll ensure you understand how they function.

Output:

0:00
/0:19

Conclusion

Overall, creating a multiple file uploader with React and Material-UI can greatly enhance the user experience of your web application. With the right packages and libraries, you can make file uploads not only functional but also visually appealing and intuitive.

By following the code and explanations provided in this blog post, you can implement a robust and user-friendly multiple-file uploader in your projects. We hope this guide has been informative and helps you create a seamless file upload experience for your users. Happy coding!

Mulecraft Footer