Utils
use-upload-monitor
Monitor file uploads with XMLHttpRequest progress tracking and abortable removal
Installation
$npx litefy@latest add use-upload-monitor
$pnpm dlx litefy@latest add use-upload-monitor
$yarn dlx litefy@latest add use-upload-monitor
$bun --bun litefy@latest add use-upload-monitor
Usage
Fully decoupled from the Upload component — the hook receives the items produced by onFilesAccepted and handles the network layer with XMLHttpRequest.
Click to upload or drag and dropFiles are validated locally before acceptance
"use client";
import * as React from "react";
import { List, Upload, useUploadMonitor, type UploadActions, type UploadReject } from "@/ui";
class MockXMLHttpRequest {
status = 0;
withCredentials = false;
upload = { onprogress: null as ((event: ProgressEvent) => void) | null };
onload: (() => void) | null = null;
onerror: (() => void) | null = null;
onabort: (() => void) | null = null;
private timer: ReturnType<typeof setInterval> | null = null;
private loaded = 0;
private total = 4 * 1024 * 1024;
open() {}
setRequestHeader() {}
send() {
this.timer = setInterval(() => {
this.loaded = Math.min(this.loaded + this.total / 25, this.total);
this.upload.onprogress?.({
lengthComputable: true,
loaded: this.loaded,
total: this.total,
} as ProgressEvent);
if (this.loaded >= this.total) this.finish();
}, 120);
}
abort() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
this.onabort?.();
}
}
private finish() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
this.status = 200;
this.onload?.();
}
}
export default function UploadBasicDemo() {
const actionsRef = React.useRef<UploadActions | null>(null);
const [rejects, setRejects] = React.useState<UploadReject[]>([]);
const upload = useUploadMonitor({ action: "mock://upload" });
React.useEffect(() => {
const Original = window.XMLHttpRequest;
window.XMLHttpRequest = MockXMLHttpRequest as unknown as typeof XMLHttpRequest;
return () => {
window.XMLHttpRequest = Original;
};
}, []);
return (
<div className="flex w-full max-w-md flex-col gap-4">
<Upload
multiple
maxCount={5}
maxSize={2 * 1024 * 1024}
actionsRef={actionsRef}
onFilesAccepted={upload.acceptFiles}
onFilesRejected={setRejects}
onFileRemove={upload.removeItem}
/>
{rejects.length > 0 && (
<ul className="space-y-1 text-sm text-danger">
{rejects.map((reject, index) => (
<li key={index}>{reject.message}</li>
))}
</ul>
)}
<List
className="max-h-48"
items={upload.fileList}
getKey={(item) => item.uid}
empty={
<div className="px-3 py-4 text-center text-muted-foreground">No files</div>
}
renderItem={(item) => (
<>
<span className="flex-1 truncate">{item.file.name}</span>
<span className="w-24 text-right text-muted-foreground">
{item.status === "uploading" ? `${item.progress}%` : item.status}
</span>
<button
type="button"
className="text-danger hover:underline"
onClick={() => actionsRef.current?.remove(item.uid)}
>
Remove
</button>
</>
)}
/>
</div>
);
}
import { Upload, useUploadMonitor } from "@/ui";
function Demo() {
const upload = useUploadMonitor({ action: "/api/upload" });
return (
<Upload
multiple
onFilesAccepted={upload.acceptFiles}
onFileRemove={upload.removeItem}
/>
);
}Each accepted item is uploaded immediately; xhr.upload.onprogress updates progress and moves the status from pending to uploading. Removing an item aborts its request; an aborted AbortSignal from the Upload also aborts the request.
API Reference
useUploadMonitor
function useUploadMonitor(options: UseUploadMonitorOptions): UseUploadMonitorResultUseUploadMonitorOptions
| Property | Type | Default | Description |
|---|---|---|---|
action | string | - | Upload URL; requests are sent via POST |
fieldName | string | "file" | FormData field name for the file |
headers | Record<string, string> | - | Extra request headers |
withCredentials | boolean | - | Send credentials cross-origin |
data | Record<string, string> | - | Extra FormData fields appended beside the file |
UseUploadMonitorResult
| Property | Type | Description |
|---|---|---|
fileList | UploadMonitorItem[] | Items with { uid, file, status, progress } |
acceptFiles | (items: UploadItem[]) => void | Start uploading validated items from the Upload component |
removeItem | (uid: string) => void | Abort the request and remove the item |
clearAll | () => void | Abort all requests and clear the list |
UploadMonitorItem
| Property | Type | Description |
|---|---|---|
uid | string | Unique id matching the Upload item |
file | File | The file being uploaded |
status | 'pending' | 'uploading' | 'success' | 'error' | Upload lifecycle state |
progress | number | Upload percentage, 0 – 100 |