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

react-upload-pro

v0.1.2

Published

The most feature-rich React file upload & dropzone library. Drag & drop, chunk uploads, cloud adapters, 20+ UI variants, i18n, a11y, SSR-safe.

Downloads

435

Readme

react-upload-pro

The most feature-rich React file upload & dropzone library.

Drag & drop · chunk uploads · cloud adapters · 21+ UI variants · i18n in 23 locales · a11y · SSR-safe · TypeScript-first.

npm types license bundle


Table of contents


Why react-upload-pro?

react-dropzone stops at "drop files, get a callback". react-upload-pro ships everything you need to ship a real upload feature:

  • Inputs — drag/drop, click, paste from clipboard, folder upload (recursive)
  • Upload engineinstant / manual / auto / queue modes, parallel + sequential, chunked, pause / resume / retry / cancel
  • Cloud adapters — AWS S3 (presigned), Cloudinary, Firebase Storage, Supabase, DigitalOcean Spaces, Azure Blob, GCS
  • Validation — MIME, extension, size (min/max), max files, duplicates, magic-number signatures, custom async validators
  • Preview — image / video / audio / PDF / text / Office (icon) with zoom & rotate
  • Progress — bar / circle / striped, per-file + aggregate, EWMA speed + ETA
  • 21+ UI variants across Minimal / Business / Creative / Enterprise / Layouts
  • a11y — ARIA roles, keyboard nav, focus rings, RTL
  • i18n — 23 built-in locales (en, hi, gu, fr, de, ar, zh, es, pt, ru, ja, ko, it, tr, id, bn, ur, nl, pl, vi, th, he, fa) + custom messages
  • Theming — light / dark / auto with CSS variables and a Tailwind preset
  • Framework agnostic — works with Axios / Fetch / GraphQL / REST via custom getUploadToken
  • Tree-shakeable — core ships without framer-motion or any cloud SDK

Install

# npm
npm install react-upload-pro

# yarn
yarn add react-upload-pro

# pnpm
pnpm add react-upload-pro

# bun
bun add react-upload-pro

Peer deps: react >= 17, react-dom >= 17. framer-motion is optional (only some animated variants need it).


30-second example

import { Dropzone, ThemeProvider } from "react-upload-pro";
import "react-upload-pro/styles.css";

export default function App() {
  return (
    <ThemeProvider defaultTheme="auto">
      <Dropzone
        endpoint="/api/upload"
        accept="image/*,application/pdf"
        maxSize={10 * 1024 * 1024} // 10 MB
        maxFiles={20}
        mode="auto"
        retries={3}
        onUploadSuccess={(file) => console.log("done", file)}
        onUploadError={(file, err) => console.error(err)}
      />
    </ThemeProvider>
  );
}

That's it. The component handles drag/drop, validation, progress bars, retries, previews, and gallery rendering.


Import the styles

You must import the bundled stylesheet exactly once — otherwise the dropzone renders unstyled (no dashed border, no accent color, no layout).

// Vite, CRA, Remix, Astro — anywhere with a global entry file
import "react-upload-pro/styles.css";
// Next.js App Router
// app/layout.tsx
import "react-upload-pro/styles.css";
// Next.js Pages Router
// pages/_app.tsx
import "react-upload-pro/styles.css";

The stylesheet is self-contained — it ships every utility class the components use, so you do not need Tailwind, PostCSS, or any extra build step in your app to get the demo look. If you also use Tailwind in your project, see Tailwind setup below to share design tokens.


Full sample with comments

A complete, copy-paste-ready example. Every prop is annotated so you can see exactly what each piece does — delete the bits you don't need.

// src/components/MyUploader.tsx

// 1️⃣ Core component + a couple of types we'll use in the callbacks.
import {
  Dropzone,
  ThemeProvider,
  I18nProvider,
  type UploadFile,
  type ValidationError,
} from "react-upload-pro";

// 2️⃣ Bundled stylesheet. Import it once at the top level of your app
//    (e.g. main.tsx / layout.tsx) — including it here works too.
import "react-upload-pro/styles.css";

import { useState } from "react";

export default function MyUploader() {
  // Track files that the dropzone rejects (wrong type, too big, etc.) so we
  // can show them in a toast or modal. Optional — leave it out if you don't
  // need rejection UI.
  const [rejected, setRejected] = useState<ValidationError[]>([]);

  return (
    // ThemeProvider gives you light / dark / auto. Wrap once near the root.
    <ThemeProvider defaultTheme="auto">
      {/* I18nProvider translates every internal string. 23 locales built in. */}
      <I18nProvider locale="en">
        <Dropzone
          // ───── Where the files go ─────
          // Pass `endpoint` for a standard multipart POST, OR pass `cloud`
          // (S3 / Cloudinary / Firebase / …) for direct-to-bucket uploads.
          endpoint="/api/upload"

          // ───── Validation ─────
          accept="image/*,application/pdf"     // MIME globs or extensions
          maxSize={10 * 1024 * 1024}           // 10 MB per file
          minSize={1024}                       // 1 KB per file
          maxFiles={5}                         // total file cap
          multiple                             // allow picking many at once
          rejectDuplicates                     // skip identical files

          // ───── Upload behaviour ─────
          mode="auto"          // 'manual' | 'instant' | 'auto' | 'queue'
          strategy="parallel"  // 'parallel' | 'sequential'
          concurrency={3}      // parallel upload slots
          retries={2}          // retry attempts per file on network errors
          chunkSize={5 * 1024 * 1024}  // 5 MB chunks (omit for single-shot)

          // ───── Built-in UI extras ─────
          previewable          // eye icon → fullscreen preview
          editable             // pencil icon → rename + tag
          clipboard            // allow ⌘V / Ctrl+V paste
          scrollAfter={5}      // gallery scrolls after 5 files
          maxHeight="320px"    // height of the scroll region

          // ───── Theming ─────
          // `accent` drives borders, focus rings, progress fill, primary
          // buttons, and hover states. Accepts hex (`'#10b981'`), an RGB
          // triplet (`'16 185 129'`), or any CSS color. Scoped to THIS
          // dropzone — multiple instances on one page can have different
          // accents without leaking globally.
          accent="#10b981"
          accentFg="#ffffff"   // optional — auto-derived from accent when omitted

          // ───── Copy ─────
          label="Drop your files here"
          hint="PNG, JPG, or PDF — up to 10 MB each, max 5 files"

          // ───── Events ─────
          onDrop={(accepted, rejected) =>
            console.log("dropped", { accepted, rejected })
          }
          onDropRejected={setRejected} // collect validation errors for UI
          onUploadStart={(file: UploadFile) =>
            console.log("uploading", file.name)
          }
          onUploadProgress={(file, progress) =>
            console.log(`${file.name}: ${progress.percent}%`)
          }
          onUploadSuccess={(file) =>
            console.log("✅ done →", file.url)
          }
          onUploadError={(file, err) =>
            console.error("❌", file.name, err.message)
          }
        />

        {/* Toast/banner for rejected files. Replace with your own UI. */}
        {rejected.length > 0 && (
          <ul style={{ color: "crimson", marginTop: 12 }}>
            {rejected.map((e, i) => (
              <li key={i}>
                {e.file?.name ?? "file"} — {e.message}
              </li>
            ))}
          </ul>
        )}
      </I18nProvider>
    </ThemeProvider>
  );
}

What this gives you out of the box:

  • 🖱️ Drag-drop, click-to-browse, paste from clipboard
  • 🧪 Live validation with friendly error messages
  • 📊 Per-file progress bars + speed + ETA
  • 🔁 Automatic retries with exponential backoff
  • 👁️ Fullscreen preview for images, PDFs, video, audio
  • ✏️ Inline rename + metadata edit modal
  • 🌗 Light / dark / auto themes with a single prop
  • 🌐 23-locale i18n with RTL support

Step-by-step setup

1. Vite + React

# 1. Create a Vite app
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install

# 2. Install the package
npm install react-upload-pro
// src/App.tsx
import { Dropzone } from "react-upload-pro";
import "react-upload-pro/styles.css";

export default function App() {
  return (
    <div style={{ padding: 24 }}>
      <Dropzone endpoint="/api/upload" maxSize={5 * 1024 * 1024} />
    </div>
  );
}
npm run dev   # → http://localhost:5173

2. Next.js (App Router)

npx create-next-app@latest my-app --typescript --app
cd my-app
npm install react-upload-pro
// app/upload/page.tsx
"use client";

import { Dropzone, ThemeProvider } from "react-upload-pro";
import "react-upload-pro/styles.css";

export default function UploadPage() {
  return (
    <ThemeProvider defaultTheme="auto">
      <Dropzone
        endpoint="/api/upload"
        accept="image/*"
        maxSize={10 * 1024 * 1024}
      />
    </ThemeProvider>
  );
}
// app/api/upload/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const formData = await req.formData();
  const file = formData.get("file") as File;
  // ...save to disk / S3 / Cloudinary / etc.
  return NextResponse.json({ url: "/uploads/" + file.name });
}

Why 'use client'? The component uses browser APIs (drag events, File, FileReader). The package emits a "use client" banner, so importing from a server component works — but pages that render it directly need the directive.

3. Create React App

npx create-react-app my-app --template typescript
cd my-app
npm install react-upload-pro

Same src/App.tsx as the Vite example — CRA just works.


Tailwind setup (optional)

You only need this section if your own app uses Tailwind and you want to re-use react-upload-pro's design tokens (text-rup-accent, bg-rup-bg, etc.) in your own components.

Not using Tailwind? Stop here — import "react-upload-pro/styles.css" is already enough. The bundled stylesheet ships every utility the components need.

Plug in the preset:

// tailwind.config.js
module.exports = {
  presets: [require("react-upload-pro/tailwind")],
  content: [
    "./src/**/*.{ts,tsx,js,jsx}",
    "./node_modules/react-upload-pro/dist/**/*.{js,cjs}",
  ],
};

Then anywhere in your app:

<button className="bg-rup-accent text-rup-accent-fg">Custom button</button>

The accent color follows the same --rup-accent variable the dropzone uses, so picking a new accent updates your custom UI too.


Core usage

Minimum

<Dropzone endpoint="/api/upload" />

Real-world example

import { Dropzone, type UploadFile } from "react-upload-pro";
import "react-upload-pro/styles.css";

export function ProfilePictureUploader() {
  return (
    <Dropzone
      endpoint="/api/avatar"
      accept="image/*"
      maxSize={2 * 1024 * 1024} // 2 MB
      maxFiles={1}
      multiple={false}
      mode="instant" // upload immediately on drop
      previewable // eye icon → fullscreen preview
      editable // pencil icon → rename + tag
      retries={2}
      onUploadStart={(f: UploadFile) => console.log("uploading", f.name)}
      onUploadSuccess={(f) => console.log("done", f.url)}
      onUploadError={(f, e) => alert(e.message)}
    />
  );
}

Custom label / hint

<Dropzone
  endpoint="/api/upload"
  label="Drop your resume here"
  hint="PDF or DOCX, up to 5 MB"
/>

Manual control with the render prop

<Dropzone endpoint="/api/upload" maxSize={5e6}>
  {({ getRootProps, getInputProps, files, start, isUploading }) => (
    <div>
      <div
        {...getRootProps()}
        className="border-2 border-dashed p-8 rounded-lg"
      >
        <input {...getInputProps()} />
        Drop files or click
      </div>
      <p>{files.length} queued</p>
      <button onClick={() => start()} disabled={isUploading}>
        Upload
      </button>
    </div>
  )}
</Dropzone>

Pre-built variants

21+ designs grouped into 5 categories. Every variant accepts the same options as Dropzone, so any feature works on any look.

import {
  MinimalGlass,
  BusinessCRM,
  EnterpriseDocs,
  LayoutModal,
} from "react-upload-pro/variants";

<MinimalGlass endpoint="/api/upload" accent="#6366f1" />;

| Category | Variants | | -------------- | ---------------------------------------------------------------------------------------- | | Minimal | MinimalModern, MinimalGlass, MinimalNeumorphic, MinimalMaterial, MinimalInline | | Business | BusinessCRM, BusinessDashboard, BusinessSaaS | | Creative | CreativeGradient, CreativeAnimated, CreativePremium, CreativeAvatar | | Enterprise | EnterpriseDocs, EnterpriseTeam, EnterpriseMediaLibrary, EnterpriseFullscreen | | Layouts | LayoutBox, LayoutCard, LayoutSidebar, LayoutModal, LayoutFloating |

Try them all live in the playground: npm run dev after cloning.


Hooks-first usage

For full control — no built-in UI, no gallery, just upload state and helpers — use useDropzone:

import { useDropzone, UploadGallery } from "react-upload-pro";
import "react-upload-pro/styles.css";

function MyUploader() {
  const {
    getRootProps,
    getInputProps,
    files,
    isUploading,
    isDragActive,
    start,
    pause,
    resume,
    remove,
    retry,
    clear,
  } = useDropzone({
    endpoint: "/api/upload",
    accept: { "image/*": [".png", ".jpg", ".webp"] },
    maxSize: 5 * 1024 * 1024,
    mode: "manual", // wait for explicit start()
    chunkSize: 5 * 1024 * 1024, // 5 MB chunks
    strategy: "parallel",
    concurrency: 3,
  });

  return (
    <div>
      <div
        {...getRootProps()}
        className={`border-2 border-dashed p-8 rounded-lg ${
          isDragActive ? "border-blue-500 bg-blue-50" : "border-gray-300"
        }`}
      >
        <input {...getInputProps()} />
        {isDragActive ? "Drop here…" : "Drop files or click"}
      </div>

      <UploadGallery
        files={files}
        onRemove={(f) => remove(f.id)}
        onRetry={(f) => retry(f.id)}
      />

      <div style={{ display: "flex", gap: 8 }}>
        <button onClick={() => start()} disabled={isUploading}>
          Upload
        </button>
        <button onClick={pause}>Pause</button>
        <button onClick={resume}>Resume</button>
        <button onClick={clear}>Clear</button>
      </div>
    </div>
  );
}

Cloud adapters

Upload directly to your cloud bucket without proxying through your server. Credentials never leave your backend — the adapter just consumes presigned URLs / signed tokens / SAS.

AWS S3

import { useDropzone } from "react-upload-pro";
import { createS3Adapter } from "react-upload-pro/cloud";

const s3 = createS3Adapter({
  getPresignedUrl: async (file) => {
    const res = await fetch("/api/s3/presign", {
      method: "POST",
      body: JSON.stringify({ name: file.name, type: file.type }),
    });
    return res.json(); // { url, method: 'PUT', headers? }
  },
});

useDropzone({ cloud: s3, mode: "auto" });
// /api/s3/presign (Next.js Route Handler example)
import { S3Client } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({ region: process.env.AWS_REGION });

export async function POST(req: Request) {
  const { name, type } = await req.json();
  const cmd = new PutObjectCommand({
    Bucket: process.env.S3_BUCKET,
    Key: name,
    ContentType: type,
  });
  const url = await getSignedUrl(s3, cmd, { expiresIn: 60 });
  return Response.json({ url, method: "PUT" });
}

Other adapters

import {
  createCloudinaryAdapter,
  createFirebaseStorageAdapter,
  createSupabaseAdapter,
  createDigitalOceanAdapter,
  createAzureBlobAdapter,
  createGcsAdapter,
} from "react-upload-pro/cloud";

Every adapter has the same shape — pass it to cloud: on Dropzone or useDropzone.


Validation

useDropzone({
  accept: { "image/*": [".png", ".jpg"] },
  minSize: 1024, // 1 KB
  maxSize: 5 * 1024 * 1024, // 5 MB
  maxFiles: 10,
  rejectDuplicates: true, // same name + size + lastModified
  validators: [
    async (file) =>
      file.name.includes(" ")
        ? { code: "custom", message: "No spaces in filename" }
        : null,
  ],
});

For security-sensitive flows, validate the real magic number instead of trusting the MIME the browser reports:

import { detectSignature } from "react-upload-pro";

const actual = await detectSignature(file); // → 'image/png' | 'application/pdf' | ...
if (!actual) throw new Error("unknown file type");

Showing rejection errors as a modal

import {
  Dropzone,
  ValidationErrorsModal,
  type ValidationError,
} from "react-upload-pro";
import { useState } from "react";

function App() {
  const [errors, setErrors] = useState<ValidationError[]>([]);
  return (
    <>
      <Dropzone endpoint="/api/upload" onDropRejected={setErrors} />
      <ValidationErrorsModal
        open={errors.length > 0}
        errors={errors}
        onClose={() => setErrors([])}
      />
    </>
  );
}

Internationalization (i18n)

23 built-in locales — wrap with I18nProvider and the dropzone UI translates automatically:

import { I18nProvider, Dropzone } from "react-upload-pro";

<I18nProvider locale="ja">
  <Dropzone endpoint="/api/upload" />
</I18nProvider>;

Supported locales

| Code | Language | RTL | | ---- | ---------------- | --- | | en | English | | | es | Español | | | fr | Français | | | de | Deutsch | | | it | Italiano | | | pt | Português | | | nl | Nederlands | | | pl | Polski | | | ru | Русский | | | tr | Türkçe | | | zh | 中文 | | | ja | 日本語 | | | ko | 한국어 | | | vi | Tiếng Việt | | | th | ไทย | | | id | Bahasa Indonesia | | | hi | हिन्दी | | | gu | ગુજરાતી | | | bn | বাংলা | | | ar | العربية | ✓ | | ur | اردو | ✓ | | he | עברית | ✓ | | fa | فارسی | ✓ |

Custom messages

Override any string per-locale:

<I18nProvider
  locale="en"
  messages={{ dropHere: "Drop your resume here", browse: "Pick a PDF" }}
>
  <Dropzone endpoint="/api/upload" />
</I18nProvider>

Check the RTL set programmatically

import { rtlLocales } from "react-upload-pro";

const isRtl = rtlLocales.has(currentLocale);

Theming

import { ThemeProvider, useTheme } from "react-upload-pro";

<ThemeProvider defaultTheme="auto">
  {" "}
  {/* 'light' | 'dark' | 'auto' */}
  <Dropzone endpoint="/api/upload" />
</ThemeProvider>;

Custom accent color

{/* Hex string — most common */}
<Dropzone endpoint="/api/upload" accent="#10b981" />

{/* RGB triplet (the format CSS variables use internally) */}
<Dropzone endpoint="/api/upload" accent="16 185 129" />

{/* Any CSS color the browser understands */}
<Dropzone endpoint="/api/upload" accent="rebeccapurple" />

{/* With a custom foreground (button text on accent background).
    Auto-derived from luminance when omitted. */}
<Dropzone endpoint="/api/upload" accent="#facc15" accentFg="#000000" />

Each accent is scoped to that dropzone instance — the value is applied as an inline --rup-accent CSS variable on the component's outer wrapper, so multiple dropzones on the same page can each have their own accent without leaking globally.

Need the accent to apply page-wide (e.g. share it with your own buttons)? Set it as a CSS variable on any ancestor:

:root {
  --rup-accent: 16 185 129; /* RGB triplet, no rgb() wrapper */
  --rup-accent-fg: 255 255 255;
}

Every variant also accepts the same prop:

import { MinimalGlass, BusinessCRM } from "react-upload-pro/variants";

<MinimalGlass accent="#6366f1" endpoint="/api/upload" />
<BusinessCRM accent="#10b981" endpoint="/api/upload" />

The accent drives borders, buttons, progress fill, focus rings, hover states, and scrollbars.


Upload modes & strategies

| Option | Values | Default | Description | | ---------------- | -------------------------------------------------------------- | ------------ | ------------------------- | | mode | 'manual' | 'instant' | 'auto' | 'queue' | 'manual' | When to start uploading | | strategy | 'parallel' | 'sequential' | 'parallel' | How to dispatch the queue | | concurrency | number | 3 | Parallel upload slots | | retries | number | 2 | Retry attempts per file | | retryBackoffMs | number | 500 | Doubles each retry | | chunkSize | number (bytes) | unset | Single-shot if unset |

Mode cheat sheet

  • manual — files queue up; user clicks an "Upload" button to start
  • instant — each file starts uploading the moment it's dropped
  • auto — same as instant, but adds a small debounce so multi-drop bursts batch nicely
  • queue — strictly one at a time, in drop order, ignoring concurrency

Props reference

<Dropzone> (most common)

| Prop | Type | Notes | | ------------------ | -------------------------------------------- | ---------------------------------------------------------------- | | endpoint | string | URL for the multipart POST. Use cloud: for direct-to-S3 etc. | | cloud | CloudAdapter | Direct cloud upload (mutually exclusive with endpoint) | | accept | string | Accept | "image/*", ".pdf,.docx", or { 'image/*': ['.png'] } | | maxSize | number | Bytes | | minSize | number | Bytes | | maxFiles | number | | | multiple | boolean | Default true | | directory | boolean | Folder upload (recursive) | | clipboard | boolean | Paste from clipboard. Default true | | rejectDuplicates | boolean | Default false | | disabled | boolean | | | mode | 'manual' \| 'instant' \| 'auto' \| 'queue' | Default 'manual' | | strategy | 'parallel' \| 'sequential' | Default 'parallel' | | concurrency | number | Default 3 | | retries | number | Default 2 | | chunkSize | number | Bytes. Unset = single-shot upload | | label | ReactNode | Replaces the default heading | | hint | ReactNode | Small descriptive line under the label | | previewable | boolean | Eye icon → fullscreen preview | | editable | boolean | Pencil icon → rename + tag + describe | | scrollAfter | number | List becomes scrollable above this count | | maxHeight | string | CSS height of the scrollable region | | width / height | string | Outer container CSS sizing | | accent | string | Hex / RGB triplet / CSS color. Scoped to this instance. | | accentFg | string | Foreground on accent surfaces. Auto-derived from accent. | | onDrop | (accepted, rejected) => void | | | onDropRejected | (errors) => void | | | onUploadStart | (file) => void | | | onUploadProgress | (file, progress) => void | | | onUploadSuccess | (file) => void | | | onUploadError | (file, error) => void | |

Full API surface

  • ComponentsDropzone, UploadArea, UploadButton, UploadProgress, UploadPreview, UploadGallery, UploadModal, FilePreviewModal, FileEditModal, ValidationErrorsModal
  • HooksuseDropzone, useUploader, useUploadQueue, useUploadProgress, useFilePreview
  • ProvidersThemeProvider, I18nProvider
  • CoreUploadQueue, validateFile, validateBatch, matchesAccept
  • Cloud (subpath)createS3Adapter, createCloudinaryAdapter, createFirebaseStorageAdapter, createSupabaseAdapter, createDigitalOceanAdapter, createAzureBlobAdapter, createGcsAdapter
  • Variants (subpath) — 21 named exports (see Pre-built variants)
  • UtilitiesformatBytes, formatSpeed, formatEta, formatPercent, getFileCategory, detectSignature, wrapFile, generatePreview, revokePreview, cn
  • i18n exportstranslations, rtlLocales, type Locale, type Translations

See docs/API.md for the full reference (when published).


TypeScript

Fully typed — every public API has .d.ts. The package exports both ESM and CJS with separate type declarations so it works in any modern bundler.

import type {
  DropzoneOptions,
  DropzoneState,
  UploadFile,
  UploadStatus,
  ValidationError,
  CloudAdapter,
  Locale,
  Theme,
} from "react-upload-pro";

SSR / Next.js notes

  • Every public surface is SSR-safe — DOM APIs are only touched inside useEffect.
  • The package emits a "use client" directive, so importing from a server component works transparently. Pages that render Dropzone directly still need 'use client' at the top.
  • The base CSS (react-upload-pro/styles.css) is plain CSS — import it once in your root layout.
// app/layout.tsx
import "react-upload-pro/styles.css";

Performance

  • Default render path is O(n) on file count.
  • For galleries beyond a few hundred files, use scrollAfter={N} to cap the rendered region. For thousands of files, virtualize with your own list library — the gallery layout is intentionally pluggable.
  • previewUrl is lazily generated and automatically revoked on remove / unmount, so no object-URL leaks.
  • Core is tree-shakeable. framer-motion and cloud SDKs are not imported until you opt in.

Contributing / development

git clone https://github.com/yogeshgabani/react-upload-pro.git
cd react-upload-pro
npm install

# 🎮 Interactive playground — every variant, every option, live code preview
npm run dev          # → http://localhost:5173

# Other scripts
npm run dev:lib      # tsup --watch — rebuild library on every save
npm run storybook    # individual component stories on :6006
npm test             # vitest unit + component
npm run test:e2e     # playwright smoke
npm run build        # tsup → dist/ (ESM + CJS + .d.ts)
npm run typecheck    # tsc --noEmit
npm run lint         # eslint

The playground at npm run dev is the fastest way to explore the API — every prop is wired to a UI control and the generated code updates live as you tweak.

PRs welcome — please:

  1. Open an issue first for non-trivial changes
  2. Add a test for new behavior
  3. Run npm run typecheck && npm run lint && npm test before pushing

Changelog

Every release is documented in CHANGELOG.md — features added, bugs fixed, breaking changes called out. The live playground also has a "Version history" panel under the variant gallery so you can review the release timeline right from the demo.


License

MIT © Yogesh Gabani MIT License - Copyright (c) 2026 Yogesh Gabani

Built by Yogesh Gabani.