Skip to main content

Upload Queues

Upload multiple files with bounded concurrency, item state, cancellation, and aggregate progress.

useConvexUploadQueue coordinates independent file uploads. The default maximum concurrency is three.

ts
const { items, aggregateProgress, enqueueSafe, cancelItem } = useConvexUploadQueue(
  api.files.generateUploadUrl,
  {
    maxConcurrent: 3,
    continueOnError: true,
    onQueueIdle: (items) => {
      console.info('Upload queue settled', items.length)
    },
  },
)

Enqueue files

ts
async function chooseFiles(event: Event) {
  const input = event.target as HTMLInputElement
  const files = Array.from(input.files ?? [])
  const result = await enqueueSafe(files)

  if (!result.ok) {
    queueError.value = result.error
  }
}

The queue also accepts per-item forms supported by UploadQueueEnqueueInput when files need different mutation arguments.

Render item state

vue
<template>
  <input type="file" multiple @change="chooseFiles" />
  <progress :value="aggregateProgress" max="100" />

  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.file.name }} — {{ item.status }} — {{ item.progress }}%
      <button
        v-if="item.status === 'queued' || item.status === 'pending'"
        @click="cancelItem(item.id)"
      >
        Cancel
      </button>
    </li>
  </ul>
</template>

Queue controls

MethodEffect
cancelItem(id)Cancel one queued or active item
cancelAll()Cancel all unfinished items
clearFinished()Remove success/error/cancelled rows
reset()Cancel unfinished work and clear queue state

continueOnError: false stops scheduling remaining queued items after the first error and settles those items as cancelled. A later enqueue does not silently resume the stopped batch.

Lifetime

The queue is component/app memory. It is not persistent background work. Navigating away disposes active uploads. Use server-side job records for durable processing after upload.