Skip to content

A high-performance, minimizable file upload widget built with React 19, Tailwind CSS 4, and Zustand. Features drag-and-drop, real-time progress, and client-side image compression.

Notifications You must be signed in to change notification settings

christopherldo/upload-widget-web

Repository files navigation

📤 React Upload Widget

React TypeScript TailwindCSS Vite License

A high-performance, minimizable, and feature-rich file upload widget built with React 19, Tailwind CSS 4, and Zustand.


📖 About The Project

This project is a modern, production-ready Upload Widget designed to handle file uploads with a polished user experience. It solves the common problem of managing complex file upload states (progress, error, cancellation) in web applications while maintaining a clean and unobtrusive UI.

The widget supports drag-and-drop, real-time progress tracking, and automatic image compression, all wrapped in a collapsible interface that stays out of the user's way.

✨ Key Features

  • 🚀 Drag & Drop Interface: Seamless file selection using react-dropzone.
  • 📉 Automatic Image Compression: Client-side image compression to save bandwidth and storage using HTML Canvas.
  • 📊 Real-time Progress: Visual feedback for upload progress, total size, and compressed size.
  • 🔄 Robust State Management: Powered by Zustand and Immer for handling complex asynchronous flows.
  • 🎨 Smooth Animations: Fluid transitions for collapsing/expanding and list updates using Motion.
  • ⏯️ Control: Ability to cancel and retry individual uploads.
  • 🧩 Minimizable UI: A collapsible "floating" widget that expands only when needed.

🏗️ Architecture

sequenceDiagram
    participant User
    participant Widget as UI Component
    participant Store as Zustand Store
    participant Utils as Compressor
    participant API as Backend API

    User->>Widget: Drops File(s)
    Widget->>Store: addUploads(files)
    Store->>Store: Create Upload ID & Set Status 'progress'
    Store->>Utils: compressImage(file)
    Utils-->>Store: Compressed File
    Store->>API: POST /uploads (multipart/form-data)
    loop Upload Progress
        API-->>Store: onUploadProgress
        Store-->>Widget: Update Progress Bar
    end
    alt Success
        API-->>Store: 200 OK (URL)
        Store-->>Widget: Show Success State
    else Error
        API-->>Store: Error
        Store-->>Widget: Show Error / Retry Option
    end
Loading

🛠️ Tech Stack

Core

Styling & UI

State & Data


🚀 Getting Started

Follow these steps to get a local copy running.

Prerequisites

  • Node.js (v18+ recommended)
  • npm, yarn, or pnpm
  • Backend Server: The widget expects a backend running at http://localhost:3333/uploads to handle the POST requests.

Installation

  1. Clone the repository

    git clone https://github.com/yourusername/upload-widget.git
    cd upload-widget/web
  2. Install dependencies

    npm install
    # or
    pnpm install
    # or
    yarn install
  3. Start the development server

    npm run dev
  4. Open in browser Navigate to http://localhost:5173 to see the widget in action.


💻 Usage

Development Commands

Command Description
npm run dev Starts the local development server with Vite.
npm run build Builds the project for production (TypeScript compile + Vite build).
npm run lint Runs ESLint to check for code quality issues.
npm run preview Previews the production build locally.

API Integration Example

The widget sends a POST request to the configured endpoint. Here is the expected structure:

Request:

  • Method: POST
  • URL: http://localhost:3333/uploads
  • Header: Content-Type: multipart/form-data
  • Body: file: (Binary)

Response (Expected by Widget):

{
  "url": "https://storage.example.com/uploads/image-123.png"
}

📂 Project Structure

web/
├── src/
│   ├── components/
│   │   ├── ui/               # Generic UI components (Button, Progress Bar)
│   │   ├── upload-widget*.tsx # Core widget components (Header, List, Dropzone)
│   │   └── ...
│   ├── http/                 # API integration logic
│   │   └── upload-file-to-store.ts
│   ├── store/                # Global state (Zustand)
│   │   └── uploads.ts
│   ├── utils/                # Helper functions (Compression, Formatting)
│   ├── app.tsx               # Main application entry point
│   └── main.tsx              # React DOM rendering
├── public/
├── package.json
├── tailwind.config.js
├── tsconfig.json
└── vite.config.ts

🔮 Roadmap & Improvements

Based on the current implementation, here are suggested improvements:

  1. Environment Variables: Extract the hardcoded API URL (http://localhost:3333) into a .env file (e.g., VITE_API_URL) to support different environments.
  2. Upload Queue Limit: Implement a concurrency limit to prevent freezing the browser when uploading hundreds of files simultaneously.
  3. File Type Validation: Add stricter validation props to the UploadWidget to restrict specific file types (e.g., "Images only" or "PDFs only") before they reach the state.

Made with ❤️ by ChristopherLDO

About

A high-performance, minimizable file upload widget built with React 19, Tailwind CSS 4, and Zustand. Features drag-and-drop, real-time progress, and client-side image compression.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published