npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mui-file-uploader-pro

v0.1.1

Published

A lightweight, multi-file dropzone for Material UI with progress bars and previews.

Readme

mui-file-uploader-pro

A lightweight, customizable file uploader component for React using Material-UI (MUI). Features drag-and-drop support, file previews (images, videos), progress tracking, and full theme customization.

Demo

https://winglunlam.github.io/mui-file-uploader-pro/


Showcase

Showcase


Key Features

  • 📁 Drag & Drop Support - Intuitive drag-and-drop file uploads
  • 🖼️ Smart Previews - Auto-generates previews for images and videos (first frame)
  • 🎨 Fully Customizable - Customize upload area, icons, text, and theme
  • ☁️ AWS S3 Upload - Optional multipart upload support for direct S3 uploads (optional)
  • 📦 TypeScript Support - Built with TypeScript for better developer experience
  • 🎭 Material-UI Styled - Seamless integration with MUI theme system
  • Lightweight - Minimal dependencies, fast and performant
  • 📱 Responsive - Works perfectly on all screen sizes

Installation

Prerequisites

Before installing mui-file-uploader-pro, make sure you have the required peer dependencies installed:

npm install @mui/material @mui/icons-material @emotion/react @emotion/styled

Install the Package

npm install mui-file-uploader-pro

Quick Start

import React, { useState } from 'react';
import { ThemeProvider, CssBaseline, Container, Typography, Box, Paper } from '@mui/material';
import { FileUploader, createUploaderTheme, FileStatus } from 'mui-file-uploader-pro';

const theme = createUploaderTheme({
  palette: {
    primary: { main: '#1976d2' },
    background: { default: '#f5f5f5' }
  },
});

export const App = () => {
  const [uploadStatus, setUploadStatus] = useState<string>('');

  const handleUploadClick = (files: FileStatus[], updateProgress: (fileId: string, progress: number) => void) => {
    setUploadStatus('Uploading...');
    
    // Send files to your API
    files.forEach(fileObj => {
      console.log(`Uploading: ${fileObj.file.name}`);
      
      // Simulate upload progress
      let progress = 0;
      const interval = setInterval(() => {
        progress += Math.random() * 30;
        if (progress >= 100) {
          progress = 100;
          clearInterval(interval);
        }
        updateProgress(fileObj.id, progress);
      }, 500);
    });
    
    setTimeout(() => {
      setUploadStatus('Success!');
    }, 3000);
  };

  const handleDeleteClick = (files: FileStatus[], id: string) => {
    return files.filter(f => f.id !== id);
  }

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Container maxWidth="md" sx={{ py: 8 }}>
        <Box textAlign="center" mb={6}>
          <Typography variant="h3" fontWeight="bold" gutterBottom>
            File Uploader
          </Typography>
          <Typography variant="h6" color="text.secondary">
            A lightweight, themed dropzone for Material UI.
          </Typography>
        </Box>

        <Paper elevation={3} sx={{ p: 4, borderRadius: 2 }}>
          <FileUploader
            maxFiles={5}
            accept="*"
            onUpload={handleUploadClick}
            onDelete={handleDeleteClick}
          />
          
          {uploadStatus && (
            <Typography 
              textAlign="center" 
              color="primary" 
              sx={{ mt: 2, fontWeight: 'bold' }}
            >
              {uploadStatus}
            </Typography>
          )}
        </Paper>
      </Container>
    </ThemeProvider>
  );
};

Props Documentation

FileUploader Component

The main component for file uploads with drag-and-drop support.

FileUploaderProps

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | onUpload | (files: FileStatus[], updateProgress: (fileId: string, progress: number) => void) => void | No | - | Callback triggered when user clicks the Upload button. Receives FileStatus array with file metadata and an updateProgress function to update individual file progress (0-100). Not used if s3Config is provided. | | onDelete | (files: FileStatus[], id: string) => FileStatus[] | No | - | Callback when a file is deleted. Receives current files array and file ID. Must return updated files array. | | maxFiles | number | No | 5 | Maximum number of files allowed in the uploader. | | accept | string | No | "*" | File type filter (e.g., "image/", "image/,video/*", ".pdf"). | | renderUploadArea | (isDragging: boolean) => React.ReactNode | No | - | Custom render function for the entire upload area. Receives dragging state. | | uploadAreaIcon | React.ReactNode | No | CloudUploadIcon | Custom icon for the upload area. | | uploadAreaTitle | React.ReactNode | No | "Drag & Drop files here" | Custom title text for the upload area. | | uploadAreaSubtitle | React.ReactNode | No | "or click to browse (Max {maxFiles} files)" | Custom subtitle text for the upload area. | | s3Config | S3Config | No | - | AWS S3 multipart upload configuration. If provided, enables S3 upload instead of using onUpload. See S3 Upload section for details. |

FileStatus

Object representing a file in the uploader.

interface FileStatus {
  file: File;                                    // The actual File object
  id: string;                                    // Unique identifier for the file
  progress: number;                              // Upload progress (0-100)
  status: 'pending' | 'uploading' | 'completed' | 'error';  // File status
  previewUrl?: string;                           // Preview image URL (auto-generated for images/videos)
  onDelete?: (id: string) => void;              // Delete callback
}

Progress Tracking in onUpload

The onUpload callback receives the FileStatus array with file metadata (including unique IDs) and an updateProgress callback function that lets you update individual file progress.

Basic Example with Simulated Upload

const handleUpload = (files: FileStatus[], updateProgress: (fileId: string, progress: number) => void) => {
  files.forEach(fileObj => {
    // Simulate upload progress
    for (let i = 0; i <= 100; i += 10) {
      setTimeout(() => {
        updateProgress(fileObj.id, i);
      }, i * 200);
    }
  });
};

<FileUploader
  onUpload={handleUpload}
  onDelete={handleDelete}
/>

Customization Examples

1. Basic Upload Area Customization

Customize individual elements of the upload area:

import { FileUploader } from 'mui-file-uploader-pro';
import { CloudDone } from '@mui/icons-material';

<FileUploader
  maxFiles={10}
  uploadAreaIcon={<CloudDone sx={{ fontSize: 64, color: 'success.main' }} />}
  uploadAreaTitle="Upload your files!"
  uploadAreaSubtitle="Drag files here or click to select"
  onUpload={handleUpload}
  onDelete={handleDelete}
/>

2. Complete Custom Upload Area

Provide a fully custom render function:

<FileUploader
  maxFiles={5}
  renderUploadArea={(isDragging) => (
    <Box
      sx={{
        p: 4,
        textAlign: 'center',
        backgroundColor: isDragging ? 'primary.light' : 'background.paper',
        border: isDragging ? '3px solid' : '2px dashed',
        borderColor: isDragging ? 'primary.main' : 'divider',
        borderRadius: 2,
        transition: 'all 0.2s ease'
      }}
    >
      <CloudUploadIcon sx={{ fontSize: 56, color: 'primary.main', mb: 2 }} />
      <Typography variant="h5" fontWeight="bold">
        {isDragging ? 'Drop it here!' : 'Drop files to upload'}
      </Typography>
      <Typography variant="body2" color="text.secondary" mt={1}>
        Supporting images, videos, and documents
      </Typography>
    </Box>
  )}
  onUpload={handleUpload}
  onDelete={handleDelete}
/>

3. Icon-Only Upload Area

import { CloudUpload } from '@mui/icons-material';

<FileUploader
  maxFiles={5}
  uploadAreaIcon={<CloudUpload sx={{ fontSize: 80 }} />}
  uploadAreaTitle="Drop files here"
  onUpload={handleUpload}
  onDelete={handleDelete}
/>

Theme Customization

The FileUploader integrates seamlessly with Material-UI's theming system using createUploaderTheme.

Basic Theme Customization

import { createUploaderTheme } from 'mui-file-uploader-pro';
import { ThemeProvider } from '@mui/material/styles';

const customTheme = createUploaderTheme({
  palette: {
    primary: {
      main: '#2196f3',
    },
    secondary: {
      main: '#f50057',
    },
    background: {
      default: '#fafafa',
      paper: '#ffffff',
    },
  },
});

export const App = () => (
  <ThemeProvider theme={customTheme}>
    <FileUploader
      maxFiles={5}
      onUpload={handleUpload}
      onDelete={handleDelete}
    />
  </ThemeProvider>
);

Dark Theme Example

const darkTheme = createUploaderTheme({
  palette: {
    mode: 'dark',
    primary: {
      main: '#90caf9',
    },
    background: {
      default: '#121212',
      paper: '#1e1e1e',
    },
  },
});

<ThemeProvider theme={darkTheme}>
  <FileUploader maxFiles={5} onUpload={handleUpload} />
</ThemeProvider>

Complete Example with All Features

import React, { useState } from 'react';
import {
  ThemeProvider,
  CssBaseline,
  Container,
  Typography,
  Box,
  Paper,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  Button,
} from '@mui/material';
import ImageIcon from '@mui/icons-material/Image';
import { FileStatus } from 'mui-file-uploader-pro';
import { FileUploader, createUploaderTheme } from 'mui-file-uploader-pro';

const theme = createUploaderTheme({
  palette: {
    primary: { main: '#1976d2' },
    background: { default: '#f5f5f5' },
  },
});

export const App = () => {
  const [uploadStatus, setUploadStatus] = useState<string>('');
  const [openDialog, setOpenDialog] = useState(false);
  const [selectedFile, setSelectedFile] = useState<File | null>(null);

  const handleUploadClick = async (files: FileStatus[], updateProgress: (fileId: string, progress: number) => void) => {
    setUploadStatus('Uploading...');
    
    // Upload each file and update progress
    for (const fileObj of files) {
      try {
        // Simulate API upload with progress updates
        for (let i = 0; i <= 100; i += 10) {
          updateProgress(fileObj.id, i);
          await new Promise(resolve => setTimeout(resolve, 200));
        }
      } catch (error) {
        console.error(`Upload failed for ${fileObj.file.name}`, error);
      }
    }
    
    setUploadStatus(`Successfully uploaded ${files.length} file(s)`);
    setTimeout(() => setUploadStatus(''), 3000);
  };

  const handleDeleteClick = (files: FileStatus[], id: string) => {
    setUploadStatus(`Deleted file`);
    return files.filter(f => f.id !== id);
  };

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Container maxWidth="md" sx={{ py: 8 }}>
        <Box textAlign="center" mb={6}>
          <Typography variant="h3" fontWeight="bold" gutterBottom>
            📁 File Uploader Pro
          </Typography>
          <Typography variant="h6" color="text.secondary">
            Drag, drop, preview, and upload files with ease
          </Typography>
        </Box>

        <Paper elevation={3} sx={{ p: 4, borderRadius: 2 }}>
          <FileUploader
            maxFiles={5}
            accept="image/*,video/*,.pdf"
            uploadAreaIcon={<ImageIcon sx={{ fontSize: 64, color: 'primary.main' }} />}
            uploadAreaTitle="Upload Images, Videos, or PDFs"
            uploadAreaSubtitle="Drag files here or click to browse"
            onUpload={handleUploadClick}
            onDelete={handleDeleteClick}
          />
          
          {uploadStatus && (
            <Typography 
              textAlign="center" 
              color="primary" 
              sx={{ mt: 3, fontWeight: 'bold', fontSize: '1.1rem' }}
            >
              ✓ {uploadStatus}
            </Typography>
          )}
        </Paper>
      </Container>
    </ThemeProvider>
  );
};

AWS S3 Multipart Upload (Optional)

The FileUploader supports optional AWS S3 multipart upload for large files. This feature allows you to upload directly to S3 with progress tracking.

How It Works

  1. Frontend initiates multipart upload via your backend API
  2. Backend returns an Upload ID
  3. Frontend splits file into chunks (default: 5MB each)
  4. For each chunk, frontend gets a presigned URL from your backend
  5. Frontend uploads each chunk directly to S3
  6. Frontend completes the multipart upload via your backend API

Backend Requirements

Your backend needs to provide three API endpoints:

// Initiate multipart upload
POST /api/s3/initiate
Request: { fileKey: string, mimeType: string }
Response: { uploadId: string }

// Get presigned URL for uploading a part
POST /api/s3/presigned-url
Request: { fileKey: string, partNumber: number, uploadId: string }
Response: { presignedUrl: string }

// Complete multipart upload
POST /api/s3/complete
Request: { fileKey: string, uploadId: string, parts: Array<{ partNumber: number, eTag: string }> }
Response: void

Frontend Usage

import React from 'react';
import { FileUploader } from 'mui-file-uploader-pro';

export const S3UploaderExample = () => {
  const s3Config = {
    initiateMultipartApi: async (fileKey, mimeType) => {
      const response = await fetch('/api/s3/initiate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ fileKey, mimeType })
      });
      const data = await response.json();
      return data.uploadId;
    },

    getPresignedUrlApi: async (fileKey, partNumber, uploadId) => {
      const response = await fetch('/api/s3/presigned-url', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ fileKey, partNumber, uploadId })
      });
      const data = await response.json();
      return data.presignedUrl;
    },

    completeMultipartApi: async (fileKey, uploadId, parts) => {
      await fetch('/api/s3/complete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ fileKey, uploadId, parts })
      });
    },

    generateFileKey: (file) => {
      // Custom key generation - e.g., add timestamp or user ID
      return `uploads/${Date.now()}-${file.name}`;
    },

    partSize: 5 * 1024 * 1024, // 5MB (default)
  };

  return (
    <FileUploader
      maxFiles={5}
      accept="*"
      s3Config={s3Config}
      uploadAreaTitle="Upload to S3"
      uploadAreaSubtitle="Drag files here for direct S3 upload"
    />
  );
};

FileStatus with S3 Upload

When using S3 multipart upload, FileStatus includes additional fields:

interface FileStatus {
  // ... existing fields
  file: File;
  id: string;
  progress: number;
  status: 'pending' | 'uploading' | 'completed' | 'error';
  previewUrl?: string;

  // S3 multipart specific
  s3UploadId?: string;                              // Upload ID from S3
  s3Parts?: Array<{ partNumber: number; eTag: string }>; // Uploaded parts
}

Backend Example (Node.js with Express)

const aws = require('aws-sdk');
const s3 = new aws.S3({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  region: process.env.AWS_REGION,
});

// Initiate multipart upload
app.post('/api/s3/initiate', async (req, res) => {
  const { fileKey, mimeType } = req.body;
  try {
    const params = {
      Bucket: process.env.AWS_BUCKET,
      Key: fileKey,
      ContentType: mimeType,
    };
    const multipart = await s3.createMultipartUpload(params).promise();
    res.json({ uploadId: multipart.UploadId });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get presigned URL
app.post('/api/s3/presigned-url', async (req, res) => {
  const { fileKey, partNumber, uploadId } = req.body;
  try {
    const params = {
      Bucket: process.env.AWS_BUCKET,
      Key: fileKey,
      PartNumber: partNumber,
      UploadId: uploadId,
      Expires: 3600, // 1 hour
    };
    const presignedUrl = s3.getSignedUrl('uploadPart', params);
    res.json({ presignedUrl });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Complete multipart upload
app.post('/api/s3/complete', async (req, res) => {
  const { fileKey, uploadId, parts } = req.body;
  try {
    const params = {
      Bucket: process.env.AWS_BUCKET,
      Key: fileKey,
      UploadId: uploadId,
      MultipartUpload: {
        // Map the parts to ensure keys are Uppercase
        Parts: parts.map(part => ({
          ETag: part.eTag || part.ETag, // Handle both just in case
          PartNumber: parseInt(part.partNumber || part.PartNumber)
        })).sort((a, b) => a.PartNumber - b.PartNumber) // S3 requires parts to be in order
      },
    };
    await s3.completeMultipartUpload(params).promise();
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

S3Config Props

| Prop | Type | Required | Description | |------|------|----------|-------------| | initiateMultipartApi | (fileKey: string, mimeType: string) => Promise<string> | Yes | API to initiate multipart upload. Returns Upload ID. | | getPresignedUrlApi | (fileKey: string, partNumber: number, uploadId: string) => Promise<string> | Yes | API to get presigned URL for uploading a part. | | completeMultipartApi | (fileKey: string, uploadId: string, parts: Array<{ partNumber: number; eTag: string }>) => Promise<void> | Yes | API to complete multipart upload. | | generateFileKey | (file: File) => string | No | Custom function to generate S3 key. Default: uses file name. | | partSize | number | No | Part size in bytes. Default: 5MB (5242880). |


License

MIT License - see LICENSE file for details


Contributing

Contributions are welcome! Please feel free to submit a Pull Request.