Realistic uploads without a backend, for prototypes, tests and docs.
"use client"
import { MiB, UploadError, mockAdapter } from "@uploadcn/core"
import {
Upload,
UploadDropzone,
UploadDropzoneDescription,
UploadDropzoneTitle,
UploadQueue,
} from "@/components/ui/upload"
/** Fails based on the file name so each state is easy to reproduce. */
const adapter = mockAdapter({
speed: 3 * MiB,
resumable: false,
fail: ({ file }) => {
const name = file.name.toLowerCase()
if (name.includes("forbidden")) {
return new UploadError("You don't have permission to upload here (403)", {
code: "http",
status: 403,
})
}
if (name.includes("large")) {
return new UploadError(
"The server rejected the file as too large (413)",
{ code: "http", status: 413 }
)
}
if (/\.(exe|bat|sh)$/.test(name)) {
return new UploadError("Executable files are blocked", {
code: "rejected",
})
}
return Math.random() < 0.4
? new UploadError("Connection reset", {
code: "network",
retryable: true,
})
: null
},
})
export default function ErrorStatesExample() {
return (
<Upload
adapter={adapter}
maxSize={20 * MiB}
retry={{ retries: 1, baseDelay: 800 }}
>
<UploadDropzone>
<UploadDropzoneTitle>
Drop files to trigger failures
</UploadDropzoneTitle>
<UploadDropzoneDescription>
Names with “forbidden” → 403 · “large” → 413 · .exe/.sh → blocked ·
others fail randomly (retryable)
</UploadDropzoneDescription>
</UploadDropzone>
<UploadQueue />
</Upload>
)
}
mockAdapter simulates throughput, latency, chunks, failures and resume, so you can
build and test every UI state before your backend exists.
import { MiB, UploadError, mockAdapter } from "@uploadcn/core"
const adapter = mockAdapter({
speed: 2 * MiB, // bytes per second
latency: 250, // ms before the first byte
chunkSize: 5 * MiB, // report chunk progress
resumable: true, // pause continues from the last byte
failureRate: 0.1, // 10% of attempts fail midway (retryable)
fail: ({ file, attempt }) =>
file.name.includes("forbidden")
? new UploadError("Forbidden", { code: "http", status: 403 })
: null,
})Results look like { key, url: "mock://uploads/…" }.
In tests
The engine is plain TypeScript, so you can drive it from Vitest without a DOM:
const uploader = createUploader({ adapter: mockAdapter({ latency: 0, speed: 1e9 }), network: false })
const [item] = await uploader.add([new File(["hello"], "a.txt")])
await vi.waitFor(() => expect(uploader.getItem(item.id)?.status).toBe("success"))