React
Overview
Section titled “Overview”The React client provides hooks, components, and utilities for building file upload UIs in React applications. Supports single/multi-file uploads, drag-and-drop, resumable uploads, and real-time progress tracking.
Installation
Section titled “Installation”npm install @uploadista/reactpnpm add @uploadista/reactyarn add @uploadista/reactQuick Start
Section titled “Quick Start”import { UploadistaProvider, useUpload } from "@uploadista/react";
function App() { return ( <UploadistaProvider baseUrl="https://api.example.com" storageId="my-storage" chunkSize={1024 * 1024} > <UploadComponent /> </UploadistaProvider> );}Authentication
Section titled “Authentication”The React client supports two authentication modes that integrate with your existing auth system.
Direct Mode
Section titled “Direct Mode”Use direct mode when you have your own authentication (JWT, API keys, sessions, OAuth):
import { UploadistaProvider } from "@uploadista/react";
function App() { return ( <UploadistaProvider baseUrl="https://api.example.com" storageId="my-storage" chunkSize={1024 * 1024} auth={{ mode: 'direct', getCredentials: async () => ({ headers: { 'Authorization': `Bearer ${await getAccessToken()}` } }) }} > <UploadComponent /> </UploadistaProvider> );}Common patterns for direct mode:
// With OAuth tokenauth={{ mode: 'direct', getCredentials: async () => ({ headers: { 'Authorization': `Bearer ${await oauth.getAccessToken()}` } })}}
// With API keyauth={{ mode: 'direct', getCredentials: () => ({ headers: { 'X-API-Key': process.env.REACT_APP_API_KEY } })}}
// With session cookie (browser automatically includes cookies)auth={{ mode: 'direct', getCredentials: () => ({ cookies: { 'session': getSessionId() } })}}UploadistaCloud Mode
Section titled “UploadistaCloud Mode”Use UploadistaCloud mode for automatic JWT token exchange with your auth server:
function App() { return ( <UploadistaProvider storageId="my-storage" auth={{ mode: 'uploadista-cloud', authServerUrl: '/api/auth/token', clientId: process.env.REACT_APP_CLIENT_ID }} > <UploadComponent /> </UploadistaProvider> );}
function UploadComponent() { const upload = useUpload({ onProgress: (progress) => console.log(`Progress: ${progress}%`), onSuccess: (result) => console.log("Complete:", result), });
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (file) upload.upload(file); };
return ( <div> <input type="file" onChange={handleFileSelect} />
{upload.isUploading && ( <div> <progress value={upload.state.progress} max={100} /> <span>{upload.state.progress}%</span> </div> )}
{upload.state.status === "success" && ( <p>Upload complete! File ID: {upload.state.result?.id}</p> )} </div> );}useUpload
Section titled “useUpload”Single file upload hook:
const upload = useUpload({ onProgress?: (progress, bytesUploaded, totalBytes) => void; onSuccess?: (result: UploadFile) => void; onError?: (error: Error) => void; onChunkComplete?: (chunkSize, bytesAccepted, bytesTotal) => void; onShouldRetry?: (error, retryAttempt) => boolean;});
// Returnsupload.state // { status, progress, bytesUploaded, totalBytes, error, result }upload.upload(file) // Start uploadupload.abort() // Cancel uploadupload.reset() // Reset stateupload.retry() // Retry failed uploadupload.isUploading // Booleanupload.canRetry // Booleanupload.metrics // Upload metricsuseMultiUpload
Section titled “useMultiUpload”Multiple file upload hook:
const multiUpload = useMultiUpload({ maxConcurrent?: 3, onComplete?: (results) => void,});
// ReturnsmultiUpload.state // { progress, uploading, successful, failed }multiUpload.items // Array of upload itemsmultiUpload.addFiles(files) // Add files to queuemultiUpload.startAll() // Start all uploadsmultiUpload.retry(item) // Retry specific filemultiUpload.abort(item) // Cancel specific filemultiUpload.remove(item) // Remove from listmultiUpload.clear() // Clear alluseDragDrop
Section titled “useDragDrop”Drag and drop functionality:
const dragDrop = useDragDrop({ accept?: ['image/*', '.pdf'], maxFiles?: 5, maxFileSize?: 10 * 1024 * 1024, onFilesReceived?: (files) => void,});
// ReturnsdragDrop.state // { isDragging, errors }dragDrop.dragHandlers // { onDragEnter, onDragLeave, onDragOver, onDrop }dragDrop.openFilePicker() // Open file dialogdragDrop.inputProps // Props for <input> elementComplete Example
Section titled “Complete Example”import { useMultiUpload, useDragDrop } from "@uploadista/react";
function MultiUploadComponent() { const multiUpload = useMultiUpload({ maxConcurrent: 3, onComplete: (results) => { console.log(`${results.successful.length}/${results.total} uploaded`); }, });
const dragDrop = useDragDrop({ accept: ['image/*', '.pdf'], maxFiles: 10, maxFileSize: 50 * 1024 * 1024, onFilesReceived: (files) => { multiUpload.addFiles(files); multiUpload.startAll(); }, });
return ( <div {...dragDrop.dragHandlers} style={{ border: dragDrop.state.isDragging ? "2px solid blue" : "2px dashed gray", padding: "2rem", textAlign: "center", }} > {dragDrop.state.isDragging ? ( <p>Drop files here...</p> ) : ( <> <p>Drag files here or</p> <button onClick={dragDrop.openFilePicker}>Select Files</button> <input {...dragDrop.inputProps} style={{ display: "none" }} /> </> )}
<div style={{ marginTop: "2rem" }}> <h3>Uploads ({multiUpload.items.length})</h3> {multiUpload.items.map((item) => ( <div key={item.id} style={{ marginBottom: "1rem" }}> <div>{item.file.name}</div> <div> <progress value={item.state.progress} max={100} /> <span> {item.state.progress}%</span> </div> <div> Status: {item.state.status} {item.state.status === "uploading" && ( <button onClick={() => multiUpload.abort(item)}>Cancel</button> )} {item.state.status === "error" && ( <button onClick={() => multiUpload.retry(item)}>Retry</button> )} </div> </div> ))} </div>
<div> Overall: {multiUpload.state.progress}% | Uploading: {multiUpload.state.uploading} | Successful: {multiUpload.state.successful} | Failed: {multiUpload.state.failed} </div> </div> );}Components
Section titled “Components”UploadZone (Headless)
Section titled “UploadZone (Headless)”import { UploadZone } from "@uploadista/react/components";
<UploadZone onFilesSelected={(files) => handleUpload(files)}> {({ isDragging, openFilePicker }) => ( <div onDragOver={...} onDrop={...}> {isDragging ? "Drop here" : "Drag files or click"} <button onClick={openFilePicker}>Browse</button> </div> )}</UploadZone>SimpleUploadZone (Pre-styled)
Section titled “SimpleUploadZone (Pre-styled)”import { SimpleUploadZone } from "@uploadista/react/components";
<SimpleUploadZone accept="image/*" maxFiles={5} maxFileSize={10 * 1024 * 1024} onFilesSelected={(files) => handleUpload(files)}/>Upload Compound Component
Section titled “Upload Compound Component”The Upload compound component provides a declarative, composable API for building upload UIs with full control over rendering:
import { Upload } from "@uploadista/react";
function FileUploader() { return ( <Upload multiple maxConcurrent={3} autoStart={false} onComplete={(results) => console.log("All uploads complete:", results)} > {/* Drag & Drop Zone */} <Upload.DropZone accept="image/*" maxFiles={10}> {({ isDragging, getRootProps, getInputProps, openFilePicker }) => ( <div {...getRootProps()} style={{ border: `2px dashed ${isDragging ? "blue" : "gray"}`, padding: "2rem", textAlign: "center", cursor: "pointer", }} > <input {...getInputProps()} /> {isDragging ? "Drop files here..." : "Click or drag files to upload"} <button type="button" onClick={openFilePicker}> Browse Files </button> </div> )} </Upload.DropZone>
{/* Upload Items List */} <Upload.Items> {({ items }) => items.map((item) => ( <Upload.Item key={item.id} id={item.id}> {({ file, state, abort, retry, remove }) => ( <div style={{ display: "flex", alignItems: "center", gap: "1rem" }}> <span>{file.name}</span> <progress value={state.progress} max={100} /> <span>{state.progress}%</span> <span>{state.status}</span> {state.status === "uploading" && ( <button onClick={abort}>Cancel</button> )} {state.status === "error" && ( <button onClick={retry}>Retry</button> )} <button onClick={remove}>Remove</button> </div> )} </Upload.Item> )) } </Upload.Items>
{/* Global Progress */} <Upload.Progress> {({ progress, status }) => ( <div> Overall: {progress}% - {status} </div> )} </Upload.Progress> </Upload> );}Upload Sub-Components
Section titled “Upload Sub-Components”| Component | Description |
|---|---|
Upload.DropZone | Drag & drop zone with file picker |
Upload.Items | Container for listing upload items |
Upload.Item | Individual upload item with controls |
Upload.Progress | Global upload progress |
Upload.Input | Hidden file input (alternative to DropZone) |
Upload.DropZone Props
Section titled “Upload.DropZone Props”<Upload.DropZone accept="image/*" // File type filter maxFiles={10} // Maximum files to accept maxFileSize={50 * 1024 * 1024} // Max file size in bytes disabled={false} // Disable the drop zone> {({ isDragging, isOver, getRootProps, getInputProps, openFilePicker }) => ( // Render your custom UI )}</Upload.DropZone>Upload.Item Render Props
Section titled “Upload.Item Render Props”<Upload.Item id={item.id}> {({ file, // The File object state, // { status, progress, bytesUploaded, totalBytes, error, result } abort, // Cancel this upload retry, // Retry failed upload remove, // Remove from list pause, // Pause upload resume, // Resume paused upload }) => ( // Render your custom item UI )}</Upload.Item>Utility Functions
Section titled “Utility Functions”import { formatFileSize, formatSpeed, formatDuration, validateFileType, isImageFile, createFilePreview,} from "@uploadista/react";
formatFileSize(1024000); // "1 MB"formatSpeed(102400); // "100 KB/s"formatDuration(65000); // "1m 5s"validateFileType(file, ['image/*']); // true/falseisImageFile(file); // true/falsecreateFilePreview(file); // blob: URLFlow-Based Uploads
Section titled “Flow-Based Uploads”For advanced upload scenarios with processing pipelines, use the Flow component and useFlow hook. These provide built-in support for multi-input flows, auto-discovery of input nodes, and real-time flow execution tracking.
useFlow Hook
Section titled “useFlow Hook”The useFlow hook is the primary way to interact with flows:
import { useFlow } from "@uploadista/react";
function FlowUploader() { const flow = useFlow({ flowConfig: { flowId: "image-optimization", storageId: "s3-images", }, onSuccess: (outputs) => { console.log("Flow outputs:", outputs); }, onError: (error) => { console.error("Flow failed:", error); }, onProgress: (uploadId, bytesUploaded, totalBytes) => { console.log(`Progress: ${bytesUploaded}/${totalBytes}`); }, });
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (file) flow.upload(file); };
return ( <div> <input type="file" onChange={handleFileSelect} />
{flow.isUploading && ( <div> <p>Status: {flow.state.status}</p> <progress value={flow.state.progress} max={100} /> {flow.state.currentNodeName && ( <p>Processing: {flow.state.currentNodeName}</p> )} </div> )}
{flow.state.status === "success" && ( <div> <p>Flow complete!</p> <pre>{JSON.stringify(flow.state.flowOutputs, null, 2)}</pre> </div> )} </div> );}useFlow Return Value
Section titled “useFlow Return Value”| Property | Type | Description |
|---|---|---|
state | FlowUploadState | Complete flow upload state |
inputMetadata | FlowInputMetadata[] | null | Discovered input nodes (null until loaded) |
inputStates | Map<string, InputExecutionState> | Per-input execution states |
inputs | Record<string, unknown> | Current input values |
setInput | (nodeId, value) => void | Set value for a specific input |
execute | () => Promise<void> | Execute flow with current inputs |
upload | (file) => Promise<void> | Single file upload convenience method |
abort | () => void | Cancel the current upload |
pause | () => void | Pause the current upload |
reset | () => void | Reset state and clear all inputs |
isUploading | boolean | True when uploading or processing |
isUploadingFile | boolean | True only during file upload phase |
isProcessing | boolean | True only during flow processing phase |
isDiscoveringInputs | boolean | True while discovering flow inputs |
Multi-Input Flows
Section titled “Multi-Input Flows”For flows with multiple input nodes:
function MultiInputFlow() { const flow = useFlow({ flowConfig: { flowId: "multi-source-processing", storageId: "default", }, });
return ( <div> {/* Show discovered inputs */} {flow.inputMetadata?.map((input) => ( <div key={input.nodeId}> <label>{input.nodeName}</label> <p>{input.nodeDescription}</p>
{input.inputTypeId === "streaming-input-v1" ? ( <input type="file" onChange={(e) => { const file = e.target.files?.[0]; if (file) flow.setInput(input.nodeId, file); }} /> ) : ( <input type="url" placeholder="https://example.com/file" onChange={(e) => flow.setInput(input.nodeId, e.target.value)} /> )} </div> ))}
<button onClick={flow.execute} disabled={flow.isUploading}> Execute Flow </button>
{/* Show per-input progress */} {flow.isUploading && ( <div> {Array.from(flow.inputStates.entries()).map(([nodeId, state]) => ( <div key={nodeId}> {nodeId}: {state.status} ({state.progress}%) </div> ))} </div> )} </div> );}Flow Compound Component
Section titled “Flow Compound Component”For declarative, composable flow UIs, use the Flow compound component. The Flow component works directly within UploadistaProvider - no additional providers are required:
import { UploadistaProvider, Flow } from "@uploadista/react";
function App() { return ( <UploadistaProvider baseUrl="http://localhost:3000" storageId="local"> <ImageProcessor /> </UploadistaProvider> );}
function ImageProcessor() { return ( <Flow flowId="image-optimizer" storageId="local" onSuccess={(outputs) => console.log("Complete:", outputs)} > {/* Simple drop zone for single file */} <Flow.DropZone accept="image/*"> {({ isDragging, progress, getRootProps, getInputProps }) => ( <div {...getRootProps()} style={{ border: isDragging ? "2px solid blue" : "2px dashed gray", padding: "2rem", }} > <input {...getInputProps()} /> {isDragging ? "Drop image here" : "Drag image or click to select"} {progress > 0 && <progress value={progress} max={100} />} </div> )} </Flow.DropZone>
{/* Status display */} <Flow.Status> {({ status, currentNodeName }) => ( <p> Status: {status} {currentNodeName && ` (Processing: ${currentNodeName})`} </p> )} </Flow.Status>
{/* Error display */} <Flow.Error> {({ hasError, message, reset }) => hasError && ( <div style={{ color: "red" }}> Error: {message} <button onClick={reset}>Try Again</button> </div> ) } </Flow.Error> </Flow> );}Flow Sub-Components
Section titled “Flow Sub-Components”Flow.DropZone
Section titled “Flow.DropZone”Drop zone for single-file uploads:
<Flow.DropZone accept="image/*" maxFileSize={10 * 1024 * 1024}> {({ isDragging, isOver, progress, status, getRootProps, getInputProps, openFilePicker }) => ( // Render your drop zone UI )}</Flow.DropZone>Flow.Inputs
Section titled “Flow.Inputs”Auto-discovers and lists all input nodes:
<Flow.Inputs> {({ inputs, isLoading }) => isLoading ? ( <Spinner /> ) : ( inputs.map((input) => ( <Flow.Input key={input.nodeId} nodeId={input.nodeId}> {/* Render input UI */} </Flow.Input> )) ) }</Flow.Inputs>Flow.Input
Section titled “Flow.Input”Scoped context for a specific input node:
<Flow.Input nodeId="video-input"> {({ metadata, value, setValue, state }) => ( <div> <label>{metadata.nodeName}</label>
<Flow.Input.DropZone accept="video/*"> {({ getRootProps, getInputProps }) => ( <div {...getRootProps()}> <input {...getInputProps()} /> Drop video here </div> )} </Flow.Input.DropZone>
<Flow.Input.Preview> {({ isFile, fileName, fileSize, clear }) => isFile && ( <div> {fileName} ({fileSize} bytes) <button onClick={clear}>Remove</button> </div> ) } </Flow.Input.Preview> </div> )}</Flow.Input>Flow.Input.UrlField
Section titled “Flow.Input.UrlField”URL input for remote files:
<Flow.Input nodeId="url-input"> <Flow.Input.UrlField placeholder="https://example.com/video.mp4" /></Flow.Input>Flow.Progress
Section titled “Flow.Progress”Progress display:
<Flow.Progress> {({ progress, bytesUploaded, totalBytes, status }) => ( <div> <progress value={progress} max={100} /> <span>{bytesUploaded} / {totalBytes} bytes</span> </div> )}</Flow.Progress>Flow.Status
Section titled “Flow.Status”Status display with node tracking:
<Flow.Status> {({ status, currentNodeName, currentNodeType, flowOutputs }) => ( <div> Status: {status} {currentNodeName && <span>Processing: {currentNodeName}</span>} {flowOutputs && <pre>{JSON.stringify(flowOutputs, null, 2)}</pre>} </div> )}</Flow.Status>Flow.Error
Section titled “Flow.Error”Error handling:
<Flow.Error> {({ hasError, error, message, reset }) => hasError && ( <div> <p>Error: {message}</p> <button onClick={reset}>Reset</button> </div> ) }</Flow.Error>Flow.Submit, Flow.Cancel, Flow.Reset
Section titled “Flow.Submit, Flow.Cancel, Flow.Reset”Action buttons:
<Flow.Submit disabled={!hasAllInputs}>Execute Flow</Flow.Submit><Flow.Cancel>Cancel</Flow.Cancel><Flow.Reset>Start Over</Flow.Reset>FlowManagerProvider (Advanced)
Section titled “FlowManagerProvider (Advanced)”FlowManagerProvider is already included inside UploadistaProvider, so you don’t need to add it manually for basic usage. It’s only needed if you want to access the flow manager context directly for advanced scenarios like coordinating multiple concurrent flow instances:
import { useFlowManagerContext } from "@uploadista/react";
function AdvancedFlowList() { // Access the flow manager for advanced multi-flow coordination const { getManager, releaseManager } = useFlowManagerContext();
// getManager(flowId) - Get or create a flow manager instance // releaseManager(flowId) - Release a flow manager when done}Note: For most use cases, simply use the
Flowcompound component oruseFlowhook directly withinUploadistaProvider.
useMultiFlowUpload
Section titled “useMultiFlowUpload”For managing multiple concurrent flow uploads:
import { useMultiFlowUpload } from "@uploadista/react";
function BatchProcessor() { const multiFlow = useMultiFlowUpload({ flowConfig: { flowId: "image-optimization", storageId: "s3", }, maxConcurrent: 3, });
return ( <div> <input type="file" multiple onChange={(e) => { const files = Array.from(e.target.files ?? []); files.forEach((file) => multiFlow.addUpload(file)); }} />
{multiFlow.uploads.map((upload) => ( <div key={upload.id}> {upload.file.name}: {upload.state.progress}% </div> ))} </div> );}useFlowEvents
Section titled “useFlowEvents”Listen to flow execution events:
import { useFlowEvents } from "@uploadista/react";
function FlowMonitor() { useFlowEvents({ onFlowStart: (event) => console.log("Started:", event.flowId), onNodeStart: (event) => console.log("Node:", event.nodeName), onNodeEnd: (event) => console.log("Node complete:", event.result), onFlowEnd: (event) => console.log("Outputs:", event.outputs), onFlowError: (event) => console.error("Error:", event.error), });
return null;}TypeScript Support
Section titled “TypeScript Support”Fully typed with TypeScript 5.9+:
import type { UploadState, UploadFile, UploadMetrics, FlowUploadState, FlowUploadStatus, FlowInputMetadata, UseFlowReturn, FlowProps, FlowContextValue,} from "@uploadista/react";
const state: UploadState = { status: "uploading", progress: 45, bytesUploaded: 2359296, totalBytes: 5242880, error: null, result: null,};Related Concepts
Section titled “Related Concepts”- Authentication - Authentication and permissions
- Uploadista Server - Server configuration
- Flows Engine
- Flow Nodes
- Expo