better-upload
Better Upload
Better Upload enables direct file uploads from the browser to any S3-compatible storage using pre-signed URLs. The server generates temporary signed URLs; the client uploads directly to S3 without proxying through your server.
Architecture
Browser → (1) POST /api/upload → Your Server (@better-upload/server)
↓ generates pre-signed URL
Browser → (2) PUT signed URL → S3 Bucket (AWS, R2, MinIO, etc.)
Packages
| Package | Role |
|---|---|
@better-upload/server |
Router, routes, S3 clients, object helpers |
@better-upload/client |
React hooks (useUploadFiles, useUploadFile), imperative upload functions |
Installation
npm i @better-upload/server @better-upload/client
Quick Start
Server — Next.js App Router
import { route, type Router } from '@better-upload/server';
import { toRouteHandler } from '@better-upload/server/adapters/next';
import { cloudflare } from '@better-upload/server/clients';
const router: Router = {
client: cloudflare(), // reads env vars automatically
bucketName: process.env.AWS_BUCKET_NAME!,
routes: {
images: route({
multipleFiles: true,
fileTypes: ['image/*'],
maxFileSize: 1024 * 1024 * 5, // 5MB
maxFiles: 4,
}),
},
};
export const { POST } = toRouteHandler(router);
Client — React hook
'use client'; // only in Next.js
import { useUploadFiles } from '@better-upload/client';
export function Uploader() {
const { upload, isPending, uploadedFiles } = useUploadFiles({
route: 'images', // must match the route name in the server router
});
return (
<input
type="file"
multiple
disabled={isPending}
onChange={(e) => {
if (e.target.files) upload(e.target.files);
}}
/>
);
}
The default API endpoint is /api/upload. Override with api: '/your/path' if needed.
Reference Files
Read only the file(s) relevant to the current task.
reference/server.md
Router type, route() options (single and multiple files), onBeforeUpload / onAfterSignedUrl callbacks, RejectUpload, multipart uploads, adapters (Next.js, Node.js), all S3 clients with their env vars.
reference/client.md
useUploadFiles and useUploadFile hooks — all options, return values, and events (onBeforeUpload, onUploadBegin, onUploadProgress, onUploadComplete, onUploadFail, onError, onUploadSettle). Imperative functions (uploadFile, uploadFiles). Pre-built shadcn components.
reference/helpers.md
Server-side S3 helpers from @better-upload/server/helpers: presignGetObject, getObject, headObject, listObjectsV2, putObject, deleteObject, deleteObjects, moveObject, copyObject, object tagging, and low-level multipart helpers. Client-side: formatBytes.
Examples
Focused, copy-paste examples for common patterns.
| Example | File |
|---|---|
| Full multi-route server setup | examples/server-router.md |
Auth + access control with RejectUpload |
examples/authentication.md |
| Single file upload (avatar) | examples/single-file-upload.md |
| Client metadata + Zod validation | examples/client-metadata.md |
| TanStack Query integration | examples/tanstack-query.md |
| Pre-built Dropzone component | examples/dropzone-component.md |
| Cancel / abort an upload | examples/abort-upload.md |
| Delete files from S3 | examples/delete-files.md |
| CORS configuration | examples/cors-configuration.md |