react-upload-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,736 @@
1
+ <div align="center">
2
+
3
+ # react-upload-pro
4
+
5
+ **The most feature-rich React file upload & dropzone library.**
6
+
7
+ Drag & drop · chunk uploads · cloud adapters · 21+ UI variants · i18n in 23 locales · a11y · SSR-safe · TypeScript-first.
8
+
9
+ [![npm](https://img.shields.io/npm/v/react-upload-pro.svg?color=4f46e5&style=flat-square)](https://www.npmjs.com/package/react-upload-pro)
10
+ [![types](https://img.shields.io/badge/types-included-3178c6?style=flat-square)](#typescript)
11
+ [![license](https://img.shields.io/badge/license-MIT-emerald?style=flat-square)](./LICENSE)
12
+ [![bundle](https://img.shields.io/badge/tree--shakable-✓-10b981?style=flat-square)](#performance)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## Table of contents
19
+
20
+ - [Why react-upload-pro?](#why-react-upload-pro)
21
+ - [Install](#install)
22
+ - [30-second example](#30-second-example)
23
+ - [Step-by-step setup](#step-by-step-setup)
24
+ - [1. Vite + React](#1-vite--react)
25
+ - [2. Next.js (App Router)](#2-nextjs-app-router)
26
+ - [3. Create React App](#3-create-react-app)
27
+ - [Tailwind setup](#tailwind-setup)
28
+ - [Core usage](#core-usage)
29
+ - [Pre-built variants](#pre-built-variants)
30
+ - [Hooks-first usage](#hooks-first-usage)
31
+ - [Cloud adapters](#cloud-adapters)
32
+ - [Validation](#validation)
33
+ - [Internationalization (i18n)](#internationalization-i18n)
34
+ - [Theming](#theming)
35
+ - [Upload modes & strategies](#upload-modes--strategies)
36
+ - [Props reference](#props-reference)
37
+ - [TypeScript](#typescript)
38
+ - [SSR / Next.js notes](#ssr--nextjs-notes)
39
+ - [Performance](#performance)
40
+ - [Contributing / development](#contributing--development)
41
+ - [License](#license)
42
+
43
+ ---
44
+
45
+ ## Why react-upload-pro?
46
+
47
+ `react-dropzone` stops at "drop files, get a callback". `react-upload-pro` ships everything you need to ship a real upload feature:
48
+
49
+ - ✅ **Inputs** — drag/drop, click, paste from clipboard, folder upload (recursive)
50
+ - ✅ **Upload engine** — `instant` / `manual` / `auto` / `queue` modes, parallel + sequential, chunked, pause / resume / retry / cancel
51
+ - ✅ **Cloud adapters** — AWS S3 (presigned), Cloudinary, Firebase Storage, Supabase, DigitalOcean Spaces, Azure Blob, GCS
52
+ - ✅ **Validation** — MIME, extension, size (min/max), max files, duplicates, magic-number signatures, custom async validators
53
+ - ✅ **Preview** — image / video / audio / PDF / text / Office (icon) with zoom & rotate
54
+ - ✅ **Progress** — bar / circle / striped, per-file + aggregate, EWMA speed + ETA
55
+ - ✅ **21+ UI variants** across Minimal / Business / Creative / Enterprise / Layouts
56
+ - ✅ **a11y** — ARIA roles, keyboard nav, focus rings, RTL
57
+ - ✅ **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
58
+ - ✅ **Theming** — light / dark / auto with CSS variables and a Tailwind preset
59
+ - ✅ **Framework agnostic** — works with Axios / Fetch / GraphQL / REST via custom `getUploadToken`
60
+ - ✅ **Tree-shakeable** — core ships without `framer-motion` or any cloud SDK
61
+
62
+ ---
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ # npm
68
+ npm install react-upload-pro
69
+
70
+ # yarn
71
+ yarn add react-upload-pro
72
+
73
+ # pnpm
74
+ pnpm add react-upload-pro
75
+
76
+ # bun
77
+ bun add react-upload-pro
78
+ ```
79
+
80
+ Peer deps: `react >= 17`, `react-dom >= 17`. `framer-motion` is optional (only some animated variants need it).
81
+
82
+ ---
83
+
84
+ ## 30-second example
85
+
86
+ ```tsx
87
+ import { Dropzone, ThemeProvider } from "react-upload-pro";
88
+ import "react-upload-pro/styles.css";
89
+
90
+ export default function App() {
91
+ return (
92
+ <ThemeProvider defaultTheme="auto">
93
+ <Dropzone
94
+ endpoint="/api/upload"
95
+ accept="image/*,application/pdf"
96
+ maxSize={10 * 1024 * 1024} // 10 MB
97
+ maxFiles={20}
98
+ mode="auto"
99
+ retries={3}
100
+ onUploadSuccess={(file) => console.log("done", file)}
101
+ onUploadError={(file, err) => console.error(err)}
102
+ />
103
+ </ThemeProvider>
104
+ );
105
+ }
106
+ ```
107
+
108
+ That's it. The component handles drag/drop, validation, progress bars, retries, previews, and gallery rendering.
109
+
110
+ ---
111
+
112
+ ## Step-by-step setup
113
+
114
+ ### 1. Vite + React
115
+
116
+ ```bash
117
+ # 1. Create a Vite app
118
+ npm create vite@latest my-app -- --template react-ts
119
+ cd my-app
120
+ npm install
121
+
122
+ # 2. Install the package
123
+ npm install react-upload-pro
124
+ ```
125
+
126
+ ```tsx
127
+ // src/App.tsx
128
+ import { Dropzone } from "react-upload-pro";
129
+ import "react-upload-pro/styles.css";
130
+
131
+ export default function App() {
132
+ return (
133
+ <div style={{ padding: 24 }}>
134
+ <Dropzone endpoint="/api/upload" maxSize={5 * 1024 * 1024} />
135
+ </div>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ```bash
141
+ npm run dev # → http://localhost:5173
142
+ ```
143
+
144
+ ### 2. Next.js (App Router)
145
+
146
+ ```bash
147
+ npx create-next-app@latest my-app --typescript --app
148
+ cd my-app
149
+ npm install react-upload-pro
150
+ ```
151
+
152
+ ```tsx
153
+ // app/upload/page.tsx
154
+ "use client";
155
+
156
+ import { Dropzone, ThemeProvider } from "react-upload-pro";
157
+ import "react-upload-pro/styles.css";
158
+
159
+ export default function UploadPage() {
160
+ return (
161
+ <ThemeProvider defaultTheme="auto">
162
+ <Dropzone
163
+ endpoint="/api/upload"
164
+ accept="image/*"
165
+ maxSize={10 * 1024 * 1024}
166
+ />
167
+ </ThemeProvider>
168
+ );
169
+ }
170
+ ```
171
+
172
+ ```ts
173
+ // app/api/upload/route.ts
174
+ import { NextRequest, NextResponse } from "next/server";
175
+
176
+ export async function POST(req: NextRequest) {
177
+ const formData = await req.formData();
178
+ const file = formData.get("file") as File;
179
+ // ...save to disk / S3 / Cloudinary / etc.
180
+ return NextResponse.json({ url: "/uploads/" + file.name });
181
+ }
182
+ ```
183
+
184
+ > **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.
185
+
186
+ ### 3. Create React App
187
+
188
+ ```bash
189
+ npx create-react-app my-app --template typescript
190
+ cd my-app
191
+ npm install react-upload-pro
192
+ ```
193
+
194
+ Same `src/App.tsx` as the Vite example — CRA just works.
195
+
196
+ ---
197
+
198
+ ## Tailwind setup
199
+
200
+ If you use Tailwind, plug in the preset to pick up `react-upload-pro`'s CSS variables and utilities:
201
+
202
+ ```js
203
+ // tailwind.config.js
204
+ module.exports = {
205
+ presets: [require("react-upload-pro/tailwind")],
206
+ content: [
207
+ "./src/**/*.{ts,tsx,js,jsx}",
208
+ "./node_modules/react-upload-pro/dist/**/*.{js,cjs}",
209
+ ],
210
+ };
211
+ ```
212
+
213
+ Then import the base styles **once** (e.g. in your root layout / `main.tsx`):
214
+
215
+ ```ts
216
+ import "react-upload-pro/styles.css";
217
+ ```
218
+
219
+ Not using Tailwind? You can skip the preset and just import the CSS — everything still works, the preset only matters if you want to extend the design tokens.
220
+
221
+ ---
222
+
223
+ ## Core usage
224
+
225
+ ### Minimum
226
+
227
+ ```tsx
228
+ <Dropzone endpoint="/api/upload" />
229
+ ```
230
+
231
+ ### Real-world example
232
+
233
+ ```tsx
234
+ import { Dropzone, type UploadFile } from "react-upload-pro";
235
+ import "react-upload-pro/styles.css";
236
+
237
+ export function ProfilePictureUploader() {
238
+ return (
239
+ <Dropzone
240
+ endpoint="/api/avatar"
241
+ accept="image/*"
242
+ maxSize={2 * 1024 * 1024} // 2 MB
243
+ maxFiles={1}
244
+ multiple={false}
245
+ mode="instant" // upload immediately on drop
246
+ previewable // eye icon → fullscreen preview
247
+ editable // pencil icon → rename + tag
248
+ retries={2}
249
+ onUploadStart={(f: UploadFile) => console.log("uploading", f.name)}
250
+ onUploadSuccess={(f) => console.log("done", f.url)}
251
+ onUploadError={(f, e) => alert(e.message)}
252
+ />
253
+ );
254
+ }
255
+ ```
256
+
257
+ ### Custom label / hint
258
+
259
+ ```tsx
260
+ <Dropzone
261
+ endpoint="/api/upload"
262
+ label="Drop your resume here"
263
+ hint="PDF or DOCX, up to 5 MB"
264
+ />
265
+ ```
266
+
267
+ ### Manual control with the render prop
268
+
269
+ ```tsx
270
+ <Dropzone endpoint="/api/upload" maxSize={5e6}>
271
+ {({ getRootProps, getInputProps, files, start, isUploading }) => (
272
+ <div>
273
+ <div
274
+ {...getRootProps()}
275
+ className="border-2 border-dashed p-8 rounded-lg"
276
+ >
277
+ <input {...getInputProps()} />
278
+ Drop files or click
279
+ </div>
280
+ <p>{files.length} queued</p>
281
+ <button onClick={() => start()} disabled={isUploading}>
282
+ Upload
283
+ </button>
284
+ </div>
285
+ )}
286
+ </Dropzone>
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Pre-built variants
292
+
293
+ 21+ designs grouped into 5 categories. Every variant accepts the same options as `Dropzone`, so any feature works on any look.
294
+
295
+ ```tsx
296
+ import {
297
+ MinimalGlass,
298
+ BusinessCRM,
299
+ EnterpriseDocs,
300
+ LayoutModal,
301
+ } from "react-upload-pro/variants";
302
+
303
+ <MinimalGlass endpoint="/api/upload" accent="#6366f1" />;
304
+ ```
305
+
306
+ | Category | Variants |
307
+ | -------------- | ---------------------------------------------------------------------------------------- |
308
+ | **Minimal** | `MinimalModern`, `MinimalGlass`, `MinimalNeumorphic`, `MinimalMaterial`, `MinimalInline` |
309
+ | **Business** | `BusinessCRM`, `BusinessDashboard`, `BusinessSaaS` |
310
+ | **Creative** | `CreativeGradient`, `CreativeAnimated`, `CreativePremium`, `CreativeAvatar` |
311
+ | **Enterprise** | `EnterpriseDocs`, `EnterpriseTeam`, `EnterpriseMediaLibrary`, `EnterpriseFullscreen` |
312
+ | **Layouts** | `LayoutBox`, `LayoutCard`, `LayoutSidebar`, `LayoutModal`, `LayoutFloating` |
313
+
314
+ Try them all live in the playground: `npm run dev` after cloning.
315
+
316
+ ---
317
+
318
+ ## Hooks-first usage
319
+
320
+ For full control — no built-in UI, no gallery, just upload state and helpers — use `useDropzone`:
321
+
322
+ ```tsx
323
+ import { useDropzone, UploadGallery } from "react-upload-pro";
324
+ import "react-upload-pro/styles.css";
325
+
326
+ function MyUploader() {
327
+ const {
328
+ getRootProps,
329
+ getInputProps,
330
+ files,
331
+ isUploading,
332
+ isDragActive,
333
+ start,
334
+ pause,
335
+ resume,
336
+ remove,
337
+ retry,
338
+ clear,
339
+ } = useDropzone({
340
+ endpoint: "/api/upload",
341
+ accept: { "image/*": [".png", ".jpg", ".webp"] },
342
+ maxSize: 5 * 1024 * 1024,
343
+ mode: "manual", // wait for explicit start()
344
+ chunkSize: 5 * 1024 * 1024, // 5 MB chunks
345
+ strategy: "parallel",
346
+ concurrency: 3,
347
+ });
348
+
349
+ return (
350
+ <div>
351
+ <div
352
+ {...getRootProps()}
353
+ className={`border-2 border-dashed p-8 rounded-lg ${
354
+ isDragActive ? "border-blue-500 bg-blue-50" : "border-gray-300"
355
+ }`}
356
+ >
357
+ <input {...getInputProps()} />
358
+ {isDragActive ? "Drop here…" : "Drop files or click"}
359
+ </div>
360
+
361
+ <UploadGallery
362
+ files={files}
363
+ onRemove={(f) => remove(f.id)}
364
+ onRetry={(f) => retry(f.id)}
365
+ />
366
+
367
+ <div style={{ display: "flex", gap: 8 }}>
368
+ <button onClick={() => start()} disabled={isUploading}>
369
+ Upload
370
+ </button>
371
+ <button onClick={pause}>Pause</button>
372
+ <button onClick={resume}>Resume</button>
373
+ <button onClick={clear}>Clear</button>
374
+ </div>
375
+ </div>
376
+ );
377
+ }
378
+ ```
379
+
380
+ ---
381
+
382
+ ## Cloud adapters
383
+
384
+ 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.
385
+
386
+ ### AWS S3
387
+
388
+ ```tsx
389
+ import { useDropzone } from "react-upload-pro";
390
+ import { createS3Adapter } from "react-upload-pro/cloud";
391
+
392
+ const s3 = createS3Adapter({
393
+ getPresignedUrl: async (file) => {
394
+ const res = await fetch("/api/s3/presign", {
395
+ method: "POST",
396
+ body: JSON.stringify({ name: file.name, type: file.type }),
397
+ });
398
+ return res.json(); // { url, method: 'PUT', headers? }
399
+ },
400
+ });
401
+
402
+ useDropzone({ cloud: s3, mode: "auto" });
403
+ ```
404
+
405
+ ```ts
406
+ // /api/s3/presign (Next.js Route Handler example)
407
+ import { S3Client } from "@aws-sdk/client-s3";
408
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
409
+ import { PutObjectCommand } from "@aws-sdk/client-s3";
410
+
411
+ const s3 = new S3Client({ region: process.env.AWS_REGION });
412
+
413
+ export async function POST(req: Request) {
414
+ const { name, type } = await req.json();
415
+ const cmd = new PutObjectCommand({
416
+ Bucket: process.env.S3_BUCKET,
417
+ Key: name,
418
+ ContentType: type,
419
+ });
420
+ const url = await getSignedUrl(s3, cmd, { expiresIn: 60 });
421
+ return Response.json({ url, method: "PUT" });
422
+ }
423
+ ```
424
+
425
+ ### Other adapters
426
+
427
+ ```ts
428
+ import {
429
+ createCloudinaryAdapter,
430
+ createFirebaseStorageAdapter,
431
+ createSupabaseAdapter,
432
+ createDigitalOceanAdapter,
433
+ createAzureBlobAdapter,
434
+ createGcsAdapter,
435
+ } from "react-upload-pro/cloud";
436
+ ```
437
+
438
+ Every adapter has the same shape — pass it to `cloud:` on `Dropzone` or `useDropzone`.
439
+
440
+ ---
441
+
442
+ ## Validation
443
+
444
+ ```ts
445
+ useDropzone({
446
+ accept: { "image/*": [".png", ".jpg"] },
447
+ minSize: 1024, // 1 KB
448
+ maxSize: 5 * 1024 * 1024, // 5 MB
449
+ maxFiles: 10,
450
+ rejectDuplicates: true, // same name + size + lastModified
451
+ validators: [
452
+ async (file) =>
453
+ file.name.includes(" ")
454
+ ? { code: "custom", message: "No spaces in filename" }
455
+ : null,
456
+ ],
457
+ });
458
+ ```
459
+
460
+ For security-sensitive flows, validate the real magic number instead of trusting the MIME the browser reports:
461
+
462
+ ```ts
463
+ import { detectSignature } from "react-upload-pro";
464
+
465
+ const actual = await detectSignature(file); // → 'image/png' | 'application/pdf' | ...
466
+ if (!actual) throw new Error("unknown file type");
467
+ ```
468
+
469
+ ### Showing rejection errors as a modal
470
+
471
+ ```tsx
472
+ import {
473
+ Dropzone,
474
+ ValidationErrorsModal,
475
+ type ValidationError,
476
+ } from "react-upload-pro";
477
+ import { useState } from "react";
478
+
479
+ function App() {
480
+ const [errors, setErrors] = useState<ValidationError[]>([]);
481
+ return (
482
+ <>
483
+ <Dropzone endpoint="/api/upload" onDropRejected={setErrors} />
484
+ <ValidationErrorsModal
485
+ open={errors.length > 0}
486
+ errors={errors}
487
+ onClose={() => setErrors([])}
488
+ />
489
+ </>
490
+ );
491
+ }
492
+ ```
493
+
494
+ ---
495
+
496
+ ## Internationalization (i18n)
497
+
498
+ 23 built-in locales — wrap with `I18nProvider` and the dropzone UI translates automatically:
499
+
500
+ ```tsx
501
+ import { I18nProvider, Dropzone } from "react-upload-pro";
502
+
503
+ <I18nProvider locale="ja">
504
+ <Dropzone endpoint="/api/upload" />
505
+ </I18nProvider>;
506
+ ```
507
+
508
+ ### Supported locales
509
+
510
+ | Code | Language | RTL |
511
+ | ---- | ---------------- | --- |
512
+ | `en` | English | |
513
+ | `es` | Español | |
514
+ | `fr` | Français | |
515
+ | `de` | Deutsch | |
516
+ | `it` | Italiano | |
517
+ | `pt` | Português | |
518
+ | `nl` | Nederlands | |
519
+ | `pl` | Polski | |
520
+ | `ru` | Русский | |
521
+ | `tr` | Türkçe | |
522
+ | `zh` | 中文 | |
523
+ | `ja` | 日本語 | |
524
+ | `ko` | 한국어 | |
525
+ | `vi` | Tiếng Việt | |
526
+ | `th` | ไทย | |
527
+ | `id` | Bahasa Indonesia | |
528
+ | `hi` | हिन्दी | |
529
+ | `gu` | ગુજરાતી | |
530
+ | `bn` | বাংলা | |
531
+ | `ar` | العربية | ✓ |
532
+ | `ur` | اردو | ✓ |
533
+ | `he` | עברית | ✓ |
534
+ | `fa` | فارسی | ✓ |
535
+
536
+ ### Custom messages
537
+
538
+ Override any string per-locale:
539
+
540
+ ```tsx
541
+ <I18nProvider
542
+ locale="en"
543
+ messages={{ dropHere: "Drop your resume here", browse: "Pick a PDF" }}
544
+ >
545
+ <Dropzone endpoint="/api/upload" />
546
+ </I18nProvider>
547
+ ```
548
+
549
+ ### Check the RTL set programmatically
550
+
551
+ ```ts
552
+ import { rtlLocales } from "react-upload-pro";
553
+
554
+ const isRtl = rtlLocales.has(currentLocale);
555
+ ```
556
+
557
+ ---
558
+
559
+ ## Theming
560
+
561
+ ```tsx
562
+ import { ThemeProvider, useTheme } from "react-upload-pro";
563
+
564
+ <ThemeProvider defaultTheme="auto">
565
+ {" "}
566
+ {/* 'light' | 'dark' | 'auto' */}
567
+ <Dropzone endpoint="/api/upload" />
568
+ </ThemeProvider>;
569
+ ```
570
+
571
+ ### Custom accent color
572
+
573
+ ```tsx
574
+ <Dropzone endpoint="/api/upload" accent="#10b981" />
575
+ ```
576
+
577
+ Or via CSS variable on any ancestor:
578
+
579
+ ```css
580
+ :root {
581
+ --rup-accent: 16 185 129; /* RGB triplet, no rgb() wrapper */
582
+ }
583
+ ```
584
+
585
+ The accent drives buttons, progress, focus rings, and scrollbars.
586
+
587
+ ---
588
+
589
+ ## Upload modes & strategies
590
+
591
+ | Option | Values | Default | Description |
592
+ | ---------------- | -------------------------------------------------------------- | ------------ | ------------------------- |
593
+ | `mode` | `'manual'` &#x7C; `'instant'` &#x7C; `'auto'` &#x7C; `'queue'` | `'manual'` | When to start uploading |
594
+ | `strategy` | `'parallel'` &#x7C; `'sequential'` | `'parallel'` | How to dispatch the queue |
595
+ | `concurrency` | number | `3` | Parallel upload slots |
596
+ | `retries` | number | `2` | Retry attempts per file |
597
+ | `retryBackoffMs` | number | `500` | Doubles each retry |
598
+ | `chunkSize` | number (bytes) | unset | Single-shot if unset |
599
+
600
+ ### Mode cheat sheet
601
+
602
+ - **`manual`** — files queue up; user clicks an "Upload" button to start
603
+ - **`instant`** — each file starts uploading the moment it's dropped
604
+ - **`auto`** — same as `instant`, but adds a small debounce so multi-drop bursts batch nicely
605
+ - **`queue`** — strictly one at a time, in drop order, ignoring `concurrency`
606
+
607
+ ---
608
+
609
+ ## Props reference
610
+
611
+ ### `<Dropzone>` (most common)
612
+
613
+ | Prop | Type | Notes |
614
+ | ------------------ | -------------------------------------------- | ---------------------------------------------------------------- |
615
+ | `endpoint` | `string` | URL for the multipart `POST`. Use `cloud:` for direct-to-S3 etc. |
616
+ | `cloud` | `CloudAdapter` | Direct cloud upload (mutually exclusive with `endpoint`) |
617
+ | `accept` | `string` &#x7C; `Accept` | `"image/*"`, `".pdf,.docx"`, or `{ 'image/*': ['.png'] }` |
618
+ | `maxSize` | `number` | Bytes |
619
+ | `minSize` | `number` | Bytes |
620
+ | `maxFiles` | `number` | |
621
+ | `multiple` | `boolean` | Default `true` |
622
+ | `directory` | `boolean` | Folder upload (recursive) |
623
+ | `clipboard` | `boolean` | Paste from clipboard. Default `true` |
624
+ | `rejectDuplicates` | `boolean` | Default `false` |
625
+ | `disabled` | `boolean` | |
626
+ | `mode` | `'manual' \| 'instant' \| 'auto' \| 'queue'` | Default `'manual'` |
627
+ | `strategy` | `'parallel' \| 'sequential'` | Default `'parallel'` |
628
+ | `concurrency` | `number` | Default `3` |
629
+ | `retries` | `number` | Default `2` |
630
+ | `chunkSize` | `number` | Bytes. Unset = single-shot upload |
631
+ | `label` | `ReactNode` | Replaces the default heading |
632
+ | `hint` | `ReactNode` | Small descriptive line under the label |
633
+ | `previewable` | `boolean` | Eye icon → fullscreen preview |
634
+ | `editable` | `boolean` | Pencil icon → rename + tag + describe |
635
+ | `scrollAfter` | `number` | List becomes scrollable above this count |
636
+ | `maxHeight` | `string` | CSS height of the scrollable region |
637
+ | `width` / `height` | `string` | Outer container CSS sizing |
638
+ | `onDrop` | `(accepted, rejected) => void` | |
639
+ | `onDropRejected` | `(errors) => void` | |
640
+ | `onUploadStart` | `(file) => void` | |
641
+ | `onUploadProgress` | `(file, progress) => void` | |
642
+ | `onUploadSuccess` | `(file) => void` | |
643
+ | `onUploadError` | `(file, error) => void` | |
644
+
645
+ ### Full API surface
646
+
647
+ - **Components** — `Dropzone`, `UploadArea`, `UploadButton`, `UploadProgress`, `UploadPreview`, `UploadGallery`, `UploadModal`, `FilePreviewModal`, `FileEditModal`, `ValidationErrorsModal`
648
+ - **Hooks** — `useDropzone`, `useUploader`, `useUploadQueue`, `useUploadProgress`, `useFilePreview`
649
+ - **Providers** — `ThemeProvider`, `I18nProvider`
650
+ - **Core** — `UploadQueue`, `validateFile`, `validateBatch`, `matchesAccept`
651
+ - **Cloud (subpath)** — `createS3Adapter`, `createCloudinaryAdapter`, `createFirebaseStorageAdapter`, `createSupabaseAdapter`, `createDigitalOceanAdapter`, `createAzureBlobAdapter`, `createGcsAdapter`
652
+ - **Variants (subpath)** — 21 named exports (see [Pre-built variants](#pre-built-variants))
653
+ - **Utilities** — `formatBytes`, `formatSpeed`, `formatEta`, `formatPercent`, `getFileCategory`, `detectSignature`, `wrapFile`, `generatePreview`, `revokePreview`, `cn`
654
+ - **i18n exports** — `translations`, `rtlLocales`, type `Locale`, type `Translations`
655
+
656
+ See [`docs/API.md`](./docs/API.md) for the full reference (when published).
657
+
658
+ ---
659
+
660
+ ## TypeScript
661
+
662
+ 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.
663
+
664
+ ```ts
665
+ import type {
666
+ DropzoneOptions,
667
+ DropzoneState,
668
+ UploadFile,
669
+ UploadStatus,
670
+ ValidationError,
671
+ CloudAdapter,
672
+ Locale,
673
+ Theme,
674
+ } from "react-upload-pro";
675
+ ```
676
+
677
+ ---
678
+
679
+ ## SSR / Next.js notes
680
+
681
+ - Every public surface is **SSR-safe** — DOM APIs are only touched inside `useEffect`.
682
+ - 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.
683
+ - The base CSS (`react-upload-pro/styles.css`) is plain CSS — import it once in your root layout.
684
+
685
+ ```tsx
686
+ // app/layout.tsx
687
+ import "react-upload-pro/styles.css";
688
+ ```
689
+
690
+ ---
691
+
692
+ ## Performance
693
+
694
+ - Default render path is **O(n)** on file count.
695
+ - 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.
696
+ - `previewUrl` is lazily generated and **automatically revoked** on remove / unmount, so no object-URL leaks.
697
+ - Core is tree-shakeable. `framer-motion` and cloud SDKs are not imported until you opt in.
698
+
699
+ ---
700
+
701
+ ## Contributing / development
702
+
703
+ ```bash
704
+ git clone https://github.com/yogeshgabani/react-upload-pro.git
705
+ cd react-upload-pro
706
+ npm install
707
+
708
+ # 🎮 Interactive playground — every variant, every option, live code preview
709
+ npm run dev # → http://localhost:5173
710
+
711
+ # Other scripts
712
+ npm run dev:lib # tsup --watch — rebuild library on every save
713
+ npm run storybook # individual component stories on :6006
714
+ npm test # vitest unit + component
715
+ npm run test:e2e # playwright smoke
716
+ npm run build # tsup → dist/ (ESM + CJS + .d.ts)
717
+ npm run typecheck # tsc --noEmit
718
+ npm run lint # eslint
719
+ ```
720
+
721
+ 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.
722
+
723
+ PRs welcome — please:
724
+
725
+ 1. Open an issue first for non-trivial changes
726
+ 2. Add a test for new behavior
727
+ 3. Run `npm run typecheck && npm run lint && npm test` before pushing
728
+
729
+ ---
730
+
731
+ ## License
732
+
733
+ [MIT](./LICENSE) © Yogesh Gabani
734
+ MIT License - Copyright (c) 2026 **Yogesh Gabani**
735
+
736
+ Built by **Yogesh Gabani**.