=============== LIBRARY RULES =============== From library maintainers: - react-mediadrop is headless — it has no bundled UI components, only the useMediaDrop hook and transport contract - Do not invent APIs that aren't documented here — check apps/docs/docs/reference for the exact exported types and hook signature - Uploads require a transport implementing UploadTransport, supplied via useMediaDrop({ transport }); react-mediadrop/xhr-upload provides createXhrUploadTransport() as a reference transport ### Project Installation and Build Commands Source: https://github.com/autorender/react-mediadrop/blob/main/README.md Installs project dependencies, builds the project, runs tests, performs type checking, linting, and checks package sizes. ```sh pnpm install pnpm build pnpm test pnpm typecheck pnpm lint pnpm size # checks each published/bundled package's gzipped dist against its size budget ``` -------------------------------- ### Install react-mediadrop with pnpm Source: https://github.com/autorender/react-mediadrop/blob/main/README.md Install the react-mediadrop package using pnpm. Alternative npm and yarn commands are also provided. ```sh pnpm add react-mediadrop # or: npm install react-mediadrop # or: yarn add react-mediadrop ``` -------------------------------- ### Styling Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/styling.mdx Demonstrates how to apply custom styling to the react-mediadrop component. This example shows the flexibility in controlling the appearance of the dropzone. ```tsx import React from 'react'; import { useDropzone } from 'react-mediadrop'; function StylingExample() { const { getRootProps, getInputProps } = useDropzone({ onDrop: (acceptedFiles) => { console.log(acceptedFiles); }, }); return (

Drag and drop some files here

); } export default StylingExample; ``` -------------------------------- ### Install and Verify Project Dependencies Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/project/contributing.mdx Run these commands to install dependencies and verify the project's integrity. This sequence mirrors the checks performed by the Continuous Integration (CI) system. ```sh pnpm install pnpm lint && pnpm typecheck && pnpm test && pnpm build && pnpm size ``` -------------------------------- ### Displaying Drag States with useMediaDrop Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/drag-states.mdx This example shows how to use the `useMediaDrop` hook to get live drag and focus flags. Drag an image over the dropzone to observe the flags change. ```tsx import { useMediaDrop } from "@/index.js"; import { useState } from "react"; const DragStatesExample = () => { const [isDragAccept, setIsDragAccept] = useState(false); const [isDragReject, setIsDragReject] = useState(false); const [isDragActive, setIsDragActive] = useState(false); const [isDragGlobal, setIsDragGlobal] = useState(false); const [isDragLeave, setIsDragLeave] = useState(false); const { dragEvents } = useMediaDrop({ onDragEnter: () => { setIsDragActive(true); setIsDragLeave(false); }, onDragLeave: () => { setIsDragActive(false); setIsDragLeave(true); }, onDragAccept: () => { setIsDragAccept(true); setIsDragReject(false); }, onDragReject: () => { setIsDragAccept(false); setIsDragReject(true); }, onDragOver: (e) => { setIsDragGlobal(e.isDragGlobal); }, onDrop: () => { setIsDragActive(false); setIsDragAccept(false); setIsDragReject(false); setIsDragLeave(false); }, }); return (

Drag an image over this area.

); }; export default DragStatesExample; ``` -------------------------------- ### Run Backend and Frontend Demos Source: https://github.com/autorender/react-mediadrop/blob/main/README.md Starts the test server backend and the react-mediadrop frontend development server in separate terminals. ```sh # terminal 1 — backend, listens on http://localhost:8787 pnpm --filter test-server dev # terminal 2 — frontend pnpm --filter react-demo dev ``` -------------------------------- ### React MediaDrop Upload Progress Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(uploads)/upload-progress.mdx This example shows how to integrate upload progress tracking into your React MediaDrop application. It utilizes the `uploadFile` function and reads `progress` and `uploadStatus` from `MediaDropFile` to render a progress bar. The transport is simulated for documentation purposes. ```tsx import { MediaDropFile } from "react-mediadrop"; // Assume uploadFile is imported from your MediaDrop instance // declare function uploadFile(id: string): void; interface UploadProgressProps { file: MediaDropFile; } export function UploadProgress({ file: { id, progress, uploadStatus, size, name, type, lastModified, preview, error, data, }, }: UploadProgressProps) { const max = progress?.total ?? size; return (
{name} {type}
{uploadStatus === "uploading" ? `Uploading ${progress?.loaded ?? 0} / ${max} bytes` : uploadStatus}
); } // Example usage (within a component that has access to MediaDrop instance and files): // // const { getAcceptedFiles, uploadFile } = useMediaDrop(); // // useEffect(() => { // getAcceptedFiles().forEach(file => { // if (!file.uploadStatus) { // uploadFile(file.id); // } // }); // }, [getAcceptedFiles, uploadFile]); ``` -------------------------------- ### Install react-mediadrop Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/getting-started/installation.mdx Install the react-mediadrop package using npm. This library requires React 18 or later. For Next.js App Router projects, ensure 'use client' is added to the top of files using useMediaDrop. ```bash npm install react-mediadrop ``` -------------------------------- ### React Media Drop - File Dialog Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/file-dialog.mdx Use this example to integrate the file dialog opening functionality with your own custom button. Ensure `noClick` and `noKeyboard` are set to `true` on the dropzone component to disable its default behaviors. The `open()` method must be called directly from a user interaction, such as a click event handler, to comply with browser security policies. ```tsx import { MediaDrop } from "react-mediadrop"; function FileDialogExample() { const [files, setFiles] = React.useState([]); return ( ( )} /> ); } ``` -------------------------------- ### React Media Drop Cancel and Retry Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(uploads)/cancel-retry.mdx Demonstrates the functionality of canceling an in-flight upload and retrying a failed upload. This example is simulated for demonstration purposes. ```tsx import { useState, useCallback } from "react"; import { MediaDropzone, useUploadThing, FileRouter, } from "@uploadthing/react"; import type { OurFileRouter } from "../pages/api/uploadthing"; const maxFileCount = 3; function CancelRetryExample() { const [uploadProgress, setUploadProgress] = useState<{ [key: string]: number; }>({}); const [uploadStatus, setUploadStatus] = useState<{ [key: string]: "uploading" | "success" | "error" | "canceled"; }>({}); const { startUpload, cancelUpload, retryUpload } = useUploadThing( "fileRouter", { onUploadProgress: (p) => { setUploadProgress((prev) => ({ ...prev, [p.file.name]: p.progress })); }, onUploadError: (error) => { console.error("Upload error:", error); setUploadStatus((prev) => ({ ...prev, [error.file.name]: "error" })); }, onClientUploadComplete: (res) => { console.log("Uploads finished:", res); res.forEach((file) => { setUploadStatus((prev) => ({ ...prev, [file.name]: "success" })); }); }, onUploadBegin: (files) => { files.forEach((file) => { setUploadStatus((prev) => ({ ...prev, [file.name]: "uploading" })); }); }, onUploadCancel: (data) => { setUploadStatus((prev) => ({ ...prev, [data.file.name]: "canceled" })); }, } ); const handleCancel = useCallback( (file: File) => { cancelUpload(file.name); }, [cancelUpload] ); const handleRetry = useCallback( (file: File) => { retryUpload(file.name); }, [retryUpload] ); return ( <>
{ startUpload(files, { onClientUploadComplete: (res) => { console.log("Uploads finished:", res); res.forEach((file) => { setUploadStatus((prev) => ({ ...prev, [file.name]: "success" })); }); }, onUploadError: (error) => { console.error("Upload error:", error); setUploadStatus((prev) => ({ ...prev, [error.file.name]: "error" })); }, }); }} />
{Object.keys(uploadStatus).map((fileName) => (
{fileName} {uploadStatus[fileName] === "uploading" && ( )} {uploadStatus[fileName] === "error" && ( )} {uploadStatus[fileName] === "uploading" && ( )} {uploadStatus[fileName] === "success" && } {uploadStatus[fileName] === "canceled" && }
))}
); } export default CancelRetryExample; // Dummy FileRouter for the example // In a real app, this would be in your api/uploadthing/[router].ts file const dummyFileRouter = { fileRouter: { upload: { middleware: () => { // Simulate a 4-second upload time return new Promise((resolve) => setTimeout(() => resolve({}), 4000)); }, callback: () => { // Simulate a 10% chance of error if (Math.random() < 0.1) { throw new Error("Simulated upload error"); } return { uploadUrl: "http://localhost:3000/upload", }; }, options: { maxFileCount: 3, }, }, }, } satisfies FileRouter; export type OurFileRouter = typeof dummyFileRouter; ``` -------------------------------- ### react-mediadrop Form Integration Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/forms.mdx This example demonstrates how to mirror accepted files into a hidden input for native form submission. It utilizes the `.file` property of `MediaDropFile` to access the original browser File object. ```tsx import * as React from 'react'; import { MediaDrop } from 'react-mediadrop'; const FormsExample = () => { const [files, setFiles] = React.useState([]); const handleDrop = React.useCallback((acceptedFiles: MediaDropFile[]) => { setFiles(acceptedFiles); }, []); return (
f.file))} /> ); }; export default FormsExample; ``` -------------------------------- ### React Presigned URL Upload Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(uploads)/presigned-url.mdx This example demonstrates how to upload files directly to a presigned PUT URL using react-mediadrop. It requires resolving the presigned URL for each file before initiating the upload. ```tsx import { createXhrUploadTransport } from "@medialib/react-mediadrop"; import { useState } from "react"; const PresignedUploadExample = () => { const [files, setFiles] = useState([]); const handleFilesChange = (newFiles: File[]) => { setFiles(newFiles); }; const handleUpload = async () => { if (files.length === 0) return; const transport = createXhrUploadTransport({ endpoint: async (file) => { // Replace with your actual server endpoint to get a presigned URL const response = await fetch("/api/presigned-url", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ filename: file.name, contentType: file.type, }), }); const { url } = await response.json(); return url; }, formData: false, // Send raw bytes as the request body }); for (const file of files) { try { await transport.uploadFile(file); console.log(`Successfully uploaded ${file.name}`); } catch (error) { console.error(`Failed to upload ${file.name}:`, error); } } }; return (
handleFilesChange(Array.from(e.target.files || []))}/>
); }; export default PresignedUploadExample; ``` -------------------------------- ### Root and Input Props Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx Demonstrates how to use getRootProps and getInputProps to attach drag-and-drop and input handlers to your JSX elements. Includes aria-label and className for accessibility and styling. ```tsx
``` -------------------------------- ### Example Custom Validator Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/validation.mdx An example of a custom validator that rejects files with spaces in their names. Use custom validators for rules not covered by standard restrictions. ```typescript function validator(file: File) { if (file.name.includes(" ")) { return { code: "validator-error", message: "Filenames can't contain spaces" }; } return null; } ``` -------------------------------- ### React Media Drop Minimal Setup Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/basic.mdx This snippet shows the basic configuration for React Media Drop, including file intake and drag state management. It omits validation and upload transport for simplicity. ```tsx import * as React from 'react'; import { MediaDrop } from '@react-mediadrop/react-mediadrop'; const BasicExample = () => ( { console.log('Files dropped!'); }} onDragEnter={() => { console.log('Drag entered!'); }} onDragLeave={() => { console.log('Drag left!'); }} /> ); export default BasicExample; ``` -------------------------------- ### React Media Drop Concurrency Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(uploads)/concurrency.mdx Demonstrates setting the concurrency limit for uploads in React Media Drop. Files exceeding the cap will have an upload status of 'queued'. ```tsx import React, { useState, useCallback } from "react"; import { useMediaDrop } from "react-media-drop"; const MAX_CONCURRENT_UPLOADS = 2; function ConcurrencyExample() { const [files, setFiles] = useState([]); const handleFileDrop = useCallback((acceptedFiles) => { setFiles((prevFiles) => [ ...prevFiles, ...acceptedFiles.map((file) => ({ file, uploadStatus: "pending", // Initial status })), ]); }, []); const { getRootProps, getInputProps, isDragActive } = useMediaDrop({ onDrop: handleFileDrop, concurrency: MAX_CONCURRENT_UPLOADS, }); // Simulate upload progress and status updates React.useEffect(() => { const uploadingFiles = files.filter((f) => f.uploadStatus === "uploading"); const queuedFiles = files.filter((f) => f.uploadStatus === "queued"); if (uploadingFiles.length < MAX_CONCURRENT_UPLOADS && queuedFiles.length > 0) { setFiles((prevFiles) => { const nextFiles = [...prevFiles]; let uploadedCount = 0; for (let i = 0; i < nextFiles.length && uploadedCount < MAX_CONCURRENT_UPLOADS - uploadingFiles.length; i++) { if (nextFiles[i].uploadStatus === "queued") { nextFiles[i].uploadStatus = "uploading"; uploadedCount++; } } return nextFiles; }); } files.forEach((file, index) => { if (file.uploadStatus === "uploading") { // Simulate upload setTimeout(() => { setFiles((prevFiles) => { const updatedFiles = [...prevFiles]; updatedFiles[index].uploadStatus = "success"; // or "error" return updatedFiles; }); }, Math.random() * 3000 + 1000); // Simulate upload time } }); }, [files]); return (
{isDragActive ? (

Drop the files here ...

) : (

Drag 'n' drop some files here, or click to select files

)}
    {files.map((file, index) => (
  • {file.file.name} - Status: {file.uploadStatus}
  • ))}
); } export default ConcurrencyExample; ``` -------------------------------- ### Install Agent Skill Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/getting-started/agent-skill.mdx Use this command to add the react-mediadrop Agent Skill to your project. This allows AI coding agents to automatically pick up the skill. ```sh npx skills add autorender/react-mediadrop ``` -------------------------------- ### Install React MediaDrop Dropzone Block Source: https://github.com/autorender/react-mediadrop/blob/main/README.md Use the shadcn CLI to add the react-mediadrop dropzone block directly into your project. You can swap 'dropzone' for other available blocks like 'avatar-uploader', 'multi-file-upload-form', or 's3-direct-upload'. ```sh npx shadcn@latest add autorender/react-mediadrop/dropzone ``` -------------------------------- ### react-mediadrop — Accepting File Types Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/accept.mdx This example demonstrates how to restrict the dropzone to accept specific file types using the `restrictions.accept` property. It accepts MIME types, wildcards, or file extensions. ```tsx import { MediaDropzone } from "react-mediadrop"; function AcceptExample() { return ( console.log(files)} /> ); } ``` -------------------------------- ### Handling MediaDrop Errors Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/error-codes.mdx This example demonstrates how to use the useMediaDrop hook to handle various file restrictions and custom validation errors. It iterates through files and their associated errors, logging the error code and message. It also shows how to handle upload-specific errors. ```tsx import { useMediaDrop } from "react-mediadrop"; const { files } = useMediaDrop({ restrictions: { accept: "image/*", minSize: 1024, maxSize: 2_000_000, maxFiles: 3 }, validator: (file) => file.name.includes(" ") ? { code: "validator-error", message: "Filenames can't contain spaces" } : null, transport, // rejects to produce uploadError: { code: "upload-error", ... } }); for (const file of files) { for (const error of file.errors) { console.log(error.code, error.message); } if (file.uploadError) { console.log(file.uploadError.code, file.uploadError.message); } } ``` -------------------------------- ### Custom Validator Example Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/validator.mdx Demonstrates how to implement a custom validator function for react-mediadrop. Return null/undefined to accept files, or an error/array of errors to reject them. Avoid reimplementing basic restrictions like accept, maxSize, or minSize within this function. ```tsx import { MediaDrop } from "react-mediadrop"; function App() { const validator = (file: File) => { // Return null/undefined to accept // Return one error or an array of errors to reject if (file.size > 1000000) { return "File is too large"; } if (!file.type.startsWith("image/")) { return "Only images are allowed"; } }; return ( ); } export default App; ``` -------------------------------- ### Click-to-open and Keyboard Activation Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx By default, the root element is configured to be activatable via click and keyboard events, which triggers the native file picker. This behavior can be customized using the `noClick` and `noKeyboard` options. ```APIDOC ## Click-to-open and Keyboard Activation ### Description By default, interacting with the root element (clicking or pressing Space/Enter when focused) will open the native file picker. This behavior can be modified using specific options. ### Options - **`noClick: true`**: Disables the click-to-open functionality. The `open()` function can still be used programmatically, for example, from a separate "Choose files" button. - **`noKeyboard: true`**: Disables opening the file picker via Space/Enter key presses when the root element is focused. It also removes the `tabIndex` from the returned props and stops tracking the `isFocused` state. - **`noDrag: true`**: Disables drag and drop handling on the root element. Click-to-open and keyboard activation remain functional. ### Overriding Default Behavior If you render custom elements within the root, such as a "Choose files" button, ensure that their click events do not bubble up to the root element to prevent the file picker from opening twice. You can achieve this by calling `event.stopPropagation()` in your custom click handler. ### Example with Custom Button ```tsx
``` ``` -------------------------------- ### Custom Choose Files Button Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx Illustrates creating a custom button to trigger the file picker programmatically using the open() function. It's crucial to stop event propagation to prevent the default click-to-open behavior from firing twice. ```tsx
``` -------------------------------- ### getRootProps and getInputProps Usage Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx These functions return props that should be spread onto the root element and the hidden file input, respectively. They handle event listeners for drag-and-drop, click, and keyboard interactions, and can accept arbitrary HTML attributes for further customization. ```APIDOC ## getRootProps and getInputProps ### Description These functions provide essential props for integrating the hook's functionality with your JSX. `getRootProps` should be applied to the main dropzone container, and `getInputProps` to the hidden file input element. ### Usage Both functions accept an optional argument object where you can pass additional HTML attributes. These attributes are merged with the hook's generated props. For `getInputProps`, any `style` prop is merged, but `display: none` is enforced by the hook. ### Example ```tsx
``` ### Composing Handlers Custom event handlers can be composed with the hook's handlers. Your custom handler will execute first. If your handler calls `event.stopPropagation()`, the hook's default handling for that event will be skipped. ### Example of Composing Handlers ```tsx
console.log("also dropped", e) })}> ``` ``` -------------------------------- ### Session Store Creation and File Fingerprinting Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/upload.mdx Provides utility functions for creating in-memory or browser-backed upload session stores and for generating file fingerprints based on metadata. ```typescript createMemoryUploadSessionStore(); // in-process only — gone on reload, gone between tabs createBrowserUploadSessionStore({ prefix? }); // localStorage-backed, SSR-safe (no-op without `window`) createFileFingerprint(file: File): string; // name+size+type+lastModified+webkitRelativePath, not file contents ``` -------------------------------- ### Import useMediaDrop Hook Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx Import the useMediaDrop hook from the react-mediadrop library. ```typescript import { useMediaDrop } from "react-mediadrop"; ``` -------------------------------- ### Import necessary hooks and transport Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/getting-started/installation.mdx Import the useMediaDrop hook and the createXhrUploadTransport function from the react-mediadrop library. Remember to add 'use client' at the top of your file if using Next.js App Router. ```javascript import { useMediaDrop } from "react-mediadrop"; import { createXhrUploadTransport } from "react-mediadrop/xhr-upload"; ``` -------------------------------- ### Composing Custom Drop Handlers Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx Shows how to compose custom event handlers with the hook's default handlers. Custom handlers execute before the hook's handlers. ```tsx
console.log("also dropped", e) })}> ``` -------------------------------- ### createBrowserUploadSessionStore Function Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Creates a browser-based implementation of the MediaDropUploadSessionStore, typically using localStorage or sessionStorage. ```APIDOC ## createBrowserUploadSessionStore Function ### Description Creates a browser-based implementation of the MediaDropUploadSessionStore, typically using localStorage or sessionStorage. ### Function Signature ```typescript function createBrowserUploadSessionStore(options?: { prefix?: string }): MediaDropUploadSessionStore; ``` ``` -------------------------------- ### Import createXhrUploadTransport Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/xhr-upload.mdx Import the necessary function from the react-mediadrop/xhr-upload package. ```typescript import { createXhrUploadTransport } from "react-mediadrop/xhr-upload"; ``` -------------------------------- ### createMemoryUploadSessionStore Function Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Creates an in-memory implementation of the MediaDropUploadSessionStore. ```APIDOC ## createMemoryUploadSessionStore Function ### Description Creates an in-memory implementation of the MediaDropUploadSessionStore. ### Function Signature ```typescript function createMemoryUploadSessionStore(): MediaDropUploadSessionStore; ``` ``` -------------------------------- ### React MediaDrop Hook Initialization Source: https://github.com/autorender/react-mediadrop/blob/main/README.md Initialize the useMediaDrop hook in a React component. Configure restrictions for accepted file types and maximum number of files. ```tsx import { useMediaDrop } from "react-mediadrop"; const { getRootProps, getInputProps, files } = useMediaDrop({ restrictions: { accept: ["image/png", "image/jpeg"], maxFiles: 5 }, }); ``` -------------------------------- ### MediaDropUploadSessionStore Interface and Factory Functions Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Defines the interface for managing upload session data and provides factory functions for creating memory-based and browser-based session stores. ```typescript type MediaDropUploadSessionStore = { get(key: string): Promise; set(key: string, value: unknown): Promise; remove(key: string): Promise; }; function createMemoryUploadSessionStore(): MediaDropUploadSessionStore; function createBrowserUploadSessionStore(options?: { prefix?: string; }): MediaDropUploadSessionStore; function createFileFingerprint(file: File): string; ``` -------------------------------- ### Configure Upload Behavior Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/upload.mdx Set the maximum number of concurrent uploads, the number of retries for failed uploads, and the grace period for cancellation. ```typescript useMediaDrop({ transport, concurrency: 3, // max uploads in flight at once. Default 1 (sequential). retries: 2, // retries *after* the first attempt, shared for every file. Default 0. retryDelays: [1000, 2000, 4000], // backoff per retry; last value repeats if exhausted. cancelGraceMs: 5000, // force-free a slot this long after cancel if the transport never settles. Default 5000. }); ``` -------------------------------- ### Minimal Fetch-based Upload Transport Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/custom-transport.mdx A basic implementation of a custom upload transport using the Fetch API. It sends the file via POST and reports progress only upon completion due to Fetch's lack of built-in upload progress events. ```typescript import { createHttpError, type UploadTransport } from "react-mediadrop"; function createMyTransport(options: { endpoint: string }): UploadTransport { return { aSync upload(file, { onProgress, signal }) { const response = await fetch(options.endpoint, { method: "POST", body: file.file, signal, }); if (!response.ok) { throw createHttpError(`Upload failed: ${response.status}`, response.status); } onProgress({ loaded: file.size, total: file.size }); return { response: await response.json() }; }, }; } ``` -------------------------------- ### Create XHR Upload Transport Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/xhr-upload.mdx Instantiate the transport with an endpoint and optional fields. This transport is used with the useMediaDrop hook for managing uploads. ```typescript import { useMediaDrop } from "react-mediadrop"; import { createXhrUploadTransport } from "react-mediadrop/xhr-upload"; const transport = createXhrUploadTransport({ endpoint: "/api/upload", // or (file) => `/api/upload/${file.id}` for a per-file URL fields: { folder: "avatars" }, // extra multipart fields }); const { files } = useMediaDrop({ transport, concurrency: 3, retries: 2 }); ``` -------------------------------- ### RetryOptions and withRetry Function Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Defines options for configuring retry logic and the `withRetry` utility function for handling asynchronous operations with retries. ```typescript type RetryOptions = { retries?: number; retryDelays?: number[]; shouldRetry?: (error: unknown, attemptNumber: number) => boolean; jitter?: number; // 0–1 }; function withRetry( attempt: (attemptNumber: number) => Promise, options: RetryOptions, signal: AbortSignal, ): Promise; ``` -------------------------------- ### Add "use client" Directive for React MediaDrop Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/getting-started/installation.mdx When using `useMediaDrop` in a Next.js project with React Server Components, you must add the 'use client' directive at the top of your file. This ensures that the component runs in a Client Component environment, as `useMediaDrop` relies on hooks like `useState` and `useEffect` which are not available in Server Components. ```tsx "use client"; import { useMediaDrop } from "react-mediadrop"; ``` -------------------------------- ### createFileFingerprint Function Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Generates a unique fingerprint for a given File object, useful for identifying upload sessions. ```APIDOC ## createFileFingerprint Function ### Description Generates a unique fingerprint for a given File object, useful for identifying upload sessions. ### Function Signature ```typescript function createFileFingerprint(file: File): string; ``` ``` -------------------------------- ### useMediaDrop Hook Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx The useMediaDrop hook is a headless function that manages file drop and upload interactions. It accepts an options object to configure its behavior and returns a set of properties and methods for interacting with the file input and dropzone. ```APIDOC ## useMediaDrop Hook ### Description Provides headless functionality for file drops and uploads. Users own the markup and control the UI. ### Signature ```ts function useMediaDrop(options?: { restrictions?: MediaDropRestrictions; validator?: MediaDropValidator; noClick?: boolean; // disable click-to-open on the root noKeyboard?: boolean; // disable Space/Enter-to-open and focus tracking noDrag?: boolean; // disable the root's drag/drop handling transport?: UploadTransport; // opt into upload — see below concurrency?: number; // max uploads in flight at once. Default 1. retries?: number; // retries after the first attempt. Default 0. retryDelays?: number[]; // backoff per retry. cancelGraceMs?: number; // force-free a slot after cancel. Default 5000. }): UseMediaDropResult; ``` ### Options - **`restrictions`**: Configuration for file type and size restrictions. - **`validator`**: A custom function for validating files. - **`noClick`** (boolean): If true, disables click-to-open functionality on the root element. - **`noKeyboard`** (boolean): If true, disables keyboard activation (Space/Enter) and focus tracking. - **`noDrag`** (boolean): If true, disables drag and drop handling on the root element. - **`transport`** (UploadTransport): Enables upload functionality. See the Upload guide for details. - **`concurrency`** (number): Maximum number of concurrent uploads. Defaults to 1. - **`retries`** (number): Number of retries for failed uploads. Defaults to 0. - **`retryDelays`** (number[]): Array of delays for upload retries. - **`cancelGraceMs`** (number): Time in milliseconds to force-free a slot after a cancel. Defaults to 5000. ### Return Value (`UseMediaDropResult`) - **`files` / `acceptedFiles` / `rejectedFiles`** (`MediaDropFile[]`): An array of files, filtered by their status. - **`isDragActive` / `isDragAccept` / `isDragReject`** (`boolean`): State indicating the drag-and-drop status of the dropzone. - **`isFocused`** (`boolean`): Indicates if the root element has keyboard focus. Always `false` when `noKeyboard` is set. - **`isDragGlobal`** (`boolean`): Indicates if a file drag is happening anywhere on the document. - **`removeFile(id)` / `clearFiles()`** (`() => void`): Functions to remove individual files by ID or clear all files. Cancels in-flight uploads for removed files. - **`open()`** (`() => void`): Imperatively opens the native file picker dialog. - **`getRootProps(arg?)`** (`() => RootProps`): Returns props to be spread onto the root element, including drag/drop, click/keyboard handlers, `role`, and `tabIndex`. - **`getInputProps(arg?)`** (`() => InputProps`): Returns props for the hidden file input element. *Note: If `transport` is provided, additional upload-related methods like `uploadFile`, `uploadAll`, `cancelUpload`, `cancelAllUploads`, and `retryUpload` are also returned.* ``` -------------------------------- ### withRetry Function Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx A utility function that wraps an asynchronous operation with retry logic based on the provided options. ```APIDOC ## withRetry Function ### Description A utility function that wraps an asynchronous operation with retry logic based on the provided options. ### Function Signature ```typescript function withRetry( attempt: (attemptNumber: number) => Promise, options: RetryOptions, signal: AbortSignal, ): Promise; ``` ``` -------------------------------- ### RetryOptions Type Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Configuration options for retrying operations, including the number of retries, delay intervals, and custom retry logic. ```APIDOC ## RetryOptions Type ### Description Configuration options for retrying operations, including the number of retries, delay intervals, and custom retry logic. ### Type Definition ```typescript type RetryOptions = { retries?: number; retryDelays?: number[]; shouldRetry?: (error: unknown, attemptNumber: number) => boolean; jitter?: number; // 0–1 }; ``` ``` -------------------------------- ### React MediaDrop with XHR Upload Transport Source: https://github.com/autorender/react-mediadrop/blob/main/README.md Configure useMediaDrop with an XHR upload transport for handling file uploads. Specify the endpoint, concurrency, and retries. ```ts import { useMediaDrop } from "react-mediadrop"; import { createXhrUploadTransport } from "react-mediadrop/xhr-upload"; const { files, uploadAll } = useMediaDrop({ transport: createXhrUploadTransport({ endpoint: "/api/upload" }), concurrency: 3, retries: 2, }); ``` -------------------------------- ### createXhrUploadTransport Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/xhr-upload.mdx Creates an upload transport that sends files using XMLHttpRequest. This is useful for applications that need to display upload progress, as XMLHttpRequest provides a reliable `onprogress` event across browsers, unlike the Fetch API. ```APIDOC ## createXhrUploadTransport ### Description Creates an upload transport that sends files using `XMLHttpRequest`. This is useful for applications that need to display upload progress, as `XMLHttpRequest` provides a reliable `onprogress` event across browsers, unlike the Fetch API. ### Method Signature `createXhrUploadTransport(options: XhrUploadTransportOptions): UploadTransport` ### Options | Option | Type | Default | Notes | | --- | --- | --- | --- | | `endpoint` | `string | (file) => string` | — required | Computed per file, e.g. for a per-file presigned URL you already fetched. | | `method` | `string` | `"POST"` | | | `fieldName` | `string` | `"file"` | Ignored when `formData: false`. | | `fields` | `object | (file) => object` | — | Extra multipart fields. Ignored when `formData: false`. | | `headers` | `object | (file) => object` | — | | | `withCredentials` | `boolean` | `false` | | | `formData` | `boolean` | `true` | `false` sends the raw file body. | | `isSuccessStatus` | `(status) => boolean` | `200–299` | | | `stallTimeoutMs` | `number` | `0` (disabled) | Abort and reject if no upload progress happens for this long — a *stall* timeout (reset on every progress tick), not a flat total-duration one, so a large file on a slow-but-healthy connection is never falsely aborted. | ### Usage Example ```ts import { useMediaDrop } from "react-mediadrop"; import { createXhrUploadTransport } from "react-mediadrop/xhr-upload"; const transport = createXhrUploadTransport({ endpoint: "/api/upload", // or (file) => `/api/upload/${file.id}` for a per-file URL fields: { folder: "avatars" }, // extra multipart fields }); const { files } = useMediaDrop({ transport, concurrency: 3, retries: 2 }); ``` ### Notes - This transport is intended for generic REST-ish endpoints that you control. - It defaults to `multipart/form-data` but can send the raw file bytes if `formData: false`. - It does not include retry logic or concurrency control; these are handled by the queue (e.g., `useMediaDrop`). - It does not have a flat request timeout, only a `stallTimeoutMs` to detect lack of progress. - Uploads are not resumable; failed or canceled uploads restart from the beginning. ``` -------------------------------- ### Accessing MediaDrop Engine State Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/core-concepts.mdx Retrieve the current state of the media drop engine, which includes an array of files. This is useful for external state management or debugging. ```typescript engine.getState(); // { files: MediaDropFile[] } ``` ```typescript engine.subscribe(listener); // full-state subscription ``` ```typescript engine.subscribe(selector, listener); // fires only when selector's result changes ``` -------------------------------- ### MediaDropUploadSessionStore Interface Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/upload.mdx Defines the interface for storing upload session metadata, such as upload IDs and byte offsets, for resumable transports. ```typescript type MediaDropUploadSessionStore = { get(key: string): Promise; set(key: string, value: unknown): Promise; remove(key: string): Promise; }; ``` -------------------------------- ### MediaDropRestrictions Type Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Defines the constraints that can be applied to files during the drop process, such as size limits and accepted file types. ```APIDOC ## MediaDropRestrictions Type ### Description Defines the constraints that can be applied to files during the drop process, such as size limits and accepted file types. ### Type Definition ```typescript type MediaDropRestrictions = { maxFiles?: number; minSize?: number; // bytes maxSize?: number; // bytes accept?: string[] | string; // mime types, wildcards, or extensions }; ``` ``` -------------------------------- ### MediaDropUploadSessionStore Type Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx An interface for storing and retrieving upload session data, used for implementing custom resumable transports. ```APIDOC ## MediaDropUploadSessionStore Type ### Description An interface for storing and retrieving upload session data, used for implementing custom resumable transports. ### Type Definition ```typescript type MediaDropUploadSessionStore = { get(key: string): Promise; set(key: string, value: unknown): Promise; remove(key: string): Promise; }; ``` ``` -------------------------------- ### Retry Options Type Definition Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/upload.mdx Defines the configuration options for the retry engine, including the number of retries, delay strategy, custom retry conditions, and jitter for spreading retries. ```typescript type RetryOptions = { retries?: number; // retries after the first attempt. Default 0. retryDelays?: number[]; // backoff per retry; last value repeats if exhausted. shouldRetry?: (error: unknown, attemptNumber: number) => boolean; // default: defaultShouldRetry jitter?: number; // 0–1, randomizes each delay by up to this fraction. Default 0. }; ``` -------------------------------- ### Set Max Files in React MediaDrop Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/examples/(basics)/max-files.mdx Use the `restrictions.maxFiles` option to cap the number of files a dropzone accepts. This limit is evaluated against the entire file list, not per individual upload batch. ```tsx import { MediaDropzone } from 'react-mediadrop'; function MyDropzone() { return ( ); } ``` -------------------------------- ### MediaDropFile Type Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/types.mdx Defines the structure of a file object managed by react-mediadrop, including its metadata, status, and upload-related fields. ```typescript type MediaDropFile = { id: string; file: File; name: string; size: number; type: string; lastModified?: number; status: "idle" | "accepted" | "rejected"; errors: MediaDropError[]; // Upload fields, only set once an upload is requested: uploadStatus?: "queued" | "uploading" | "done" | "error" | "canceled"; progress?: { loaded: number; total: number | null }; uploadError?: MediaDropError; uploadResult?: unknown; uploadAttempts?: number; }; ``` -------------------------------- ### Understanding Drag State in React Media Drop Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/core-concepts.mdx Examine the different states of a drag operation within the dropzone. `isDragActive` indicates an active drag, `isDragAccept` suggests the payload is acceptable, and `isDragReject` indicates it is not. Note that `isDragAccept` and `isDragReject` rely on MIME types for accurate mid-drag evaluation. ```typescript type DragState = { isDragActive: boolean; // a drag payload is over this dropzone right now isDragAccept: boolean; // best-effort: payload looks acceptable isDragReject: boolean; // best-effort: payload looks unacceptable }; ``` -------------------------------- ### useMediaDrop Hook Signature Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/api-reference/use-media-drop.mdx Defines the signature of the useMediaDrop hook, including its optional configuration options for restrictions, validation, interaction control, and upload transport. ```typescript function useMediaDrop(options?: { restrictions?: MediaDropRestrictions; validator?: MediaDropValidator; noClick?: boolean; // disable click-to-open on the root noKeyboard?: boolean; // disable Space/Enter-to-open and focus tracking noDrag?: boolean; // disable the root's drag/drop handling transport?: UploadTransport; // opt into upload — see below concurrency?: number; // max uploads in flight at once. Default 1. retries?: number; // retries after the first attempt. Default 0. retryDelays?: number[]; // backoff per retry. cancelGraceMs?: number; // force-free a slot after cancel. Default 5000. }): UseMediaDropResult; ``` -------------------------------- ### MediaDropFile Upload Fields Type Definition Source: https://github.com/autorender/react-mediadrop/blob/main/apps/docs/docs/guides/upload.mdx Defines the structure for upload-related properties within a MediaDropFile object. Includes upload status, progress, error details, and result information. ```typescript type MediaDropFile = { // ...status, errors, etc. — unchanged from Core... uploadStatus?: "queued" | "uploading" | "done" | "error" | "canceled"; progress?: { loaded: number; total: number | null }; uploadError?: MediaDropError; // code: "upload-error", present after a failed attempt uploadResult?: unknown; // whatever the transport resolved with — opaque to the engine uploadAttempts?: number; // 1-indexed, for the current/last upload run }; ```