use-courier
| Install | |
|---|---|
npm install use-courier |
|
| Latest Version: | 1.0.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 20, 2026 |
| Links: | GitHub · npm |
use-courier
A headless React hook and Vue 3 composable for file uploads with real progress tracking, retries, cancellation, and chunked uploads for large files.
Why useCourier?
fetch can't report upload progress — there's no way to know how many bytes of a file have actually been sent, only when the whole request finishes. useCourier uses XMLHttpRequest under the hood specifically to expose real 0-100% upload progress, wrapped in a small, typed, headless hook you build your own upload UI on top of.
- Real upload progress — accurate 0-100% progress per file as bytes are actually sent, not a spinner
- Retries — re-run a failed upload without asking the user to re-select the file
- Cancellation — abort an in-flight upload, or every upload in flight on unmount
- Chunked uploads — automatically splits large files into sequential requests, retrying failed chunks independently
- Authenticated APIs — send auth headers (including async tokens), cookies, and extra form fields with every request
- Lifecycle callbacks —
beforeUpload,onUploadSuccess,onUploadError,onUploadFinish,onUploadRetry,onRemoveFile - React and Vue — the same API as a React hook (
use-courier) and a Vue 3 composable (use-courier/vue) - Headless — no UI, no styling opinions; works with any component library
- Typed — full TypeScript support, generic over your API's response shape
Installation
npm install use-courier
use-courier supports React 18 and 19, and Vue 3.3 and later. Install only the framework you use.
Basic usage
React
import type { ChangeEvent } from "react";
import { useCourier } from "use-courier";
export function FileUpload() {
const { files, addFile } = useCourier({
url: "/api/uploads",
});
function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
Array.from(event.target.files ?? []).forEach((file) => {
void addFile(file);
});
}
return (
<>
<input type="file" multiple onChange={handleFileChange} />
{files.map((item) => (
<p key={item.id}>
{item.file.name}: {item.status} ({item.uploadProgress}%)
</p>
))}
</>
);
}
Vue
<script setup lang="ts">
import { useCourier } from "use-courier/vue";
const { files, addFile } = useCourier({ url: "/api/uploads" });
function handleFileChange(event: Event) {
const input = event.target as HTMLInputElement;
Array.from(input.files ?? []).forEach((file) => addFile(file));
}
</script>
<template>
<input type="file" multiple @change="handleFileChange" />
<p v-for="item in files" :key="item.id">
{{ item.file.name }}: {{ item.status }} ({{ item.uploadProgress }}%)
</p>
</template>
Documentation
Full API reference, backend integration guides for Express, Next.js, TanStack Start, and Hono, large-file chunking details, and a Shadcn attachments integration:
The docs also publish an llms.txt and llms-full.txt for LLM tooling.
License
MIT © Zach Philipp
Related Packages
The upload client library. Simple and easy file uploads for React | Vue | Solid...
Client for Pinion — resumable uploads with fetch or Axios, retry, resume, progre...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.0 | ^3.3.0 | MIT | |
| 0.8.0 | ^3.3.0 | MIT | |
| 0.7.1 | MIT | ||
| 0.7.0 | MIT | ||
| 0.6.0 | MIT | ||
| 0.5.0 | MIT | ||
| 0.4.1 | MIT | ||
| 0.4.0 | MIT | ||
| 0.3.0 | MIT | ||
| 0.2.0 | MIT | ||
| 0.1.0 | MIT |