Mock

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"))

On this page