Skip to content

@vibe-labs/design-vue-uploads

Vue 3 file upload components for the Vibe Design System. Drop zone, file list with previews, upload queue with concurrency control, validation, auto-retry, and cancellation.

Installation

ts
import { VibeUploadZone, VibeUploadList, VibeUploadItem, VibeUploadIndicator, useUploadQueue } from "@vibe-labs/design-vue-uploads";

Requires the CSS layer from @vibe-labs/design-components-uploads.

Quick Start

vue
<script setup>
import { VibeUploadZone, VibeUploadList, VibeUploadItem, useUploadQueue } from "@vibe-labs/design-vue-uploads";

const { files, addFiles, removeFile, retryFile, cancelFile, progress, isUploading } = useUploadQueue({
  upload: async (file, { onProgress, signal }) => {
    const form = new FormData();
    form.append("file", file);
    const res = await fetch("/api/upload", { method: "POST", body: form, signal });
    return res.json(); // → stored as file.meta
  },
  concurrency: 3,
  validation: {
    accept: ["image/*", ".pdf"],
    maxFileSize: 10 * 1024 * 1024, // 10MB
    maxFiles: 20,
  },
  onValidationError: (errors) => toast.warning(errors[0].message),
});
</script>

<template>
  <VibeUploadZone accept="image/*,.pdf" @files="addFiles">
    <template #hint>Max 10MB per file · Images and PDFs</template>
  </VibeUploadZone>

  <VibeUploadList>
    <VibeUploadItem v-for="f in files" :key="f.id" :file="f" @remove="removeFile" @retry="retryFile" @cancel="cancelFile" />
  </VibeUploadList>
</template>

Components

VibeUploadZone

Drop zone with drag-and-drop, click-to-browse, and directory upload support.

vue
<!-- Basic -->
<VibeUploadZone @files="addFiles" />

<!-- With accept filter -->
<VibeUploadZone accept="image/*,.pdf" @files="addFiles" />

<!-- Single file -->
<VibeUploadZone :multiple="false" @files="addFiles" />

<!-- Compact layout -->
<VibeUploadZone compact @files="addFiles" />

<!-- Directory upload -->
<VibeUploadZone directory @files="addFiles" />

<!-- Disabled -->
<VibeUploadZone disabled />

<!-- Custom content -->
<VibeUploadZone @files="addFiles" v-slot="{ isDragOver, open }">
  <div :class="{ 'highlight': isDragOver }">
    <p>Drop files here</p>
    <button @click="open">Or click to browse</button>
  </div>
</VibeUploadZone>

<!-- Custom label and hint -->
<VibeUploadZone @files="addFiles">
  <template #label>Upload your documents</template>
  <template #hint>PDF, DOC, up to 25MB</template>
</VibeUploadZone>

Props

PropTypeDefaultDescription
acceptstringAccepted file types (HTML accept attribute)
multiplebooleantrueAllow multiple files
disabledbooleanfalseDisabled state
compactbooleanfalseCompact horizontal layout
directorybooleanfalseAllow directory upload (webkitdirectory)

Events

EventPayloadDescription
filesFileListFiles selected or dropped

Slots

SlotPropsDescription
default{ isDragOver, open }Full custom content
iconCustom upload icon
labelCustom label text
hintHint text below label

Exposed

MethodDescription
openFilePicker()Programmatically open the file dialog

VibeUploadList

Scrollable container for upload items.

vue
<VibeUploadList>
  <VibeUploadItem v-for="f in files" :key="f.id" :file="f" />
</VibeUploadList>

<!-- Unbounded (no max-height) -->
<VibeUploadList unbounded>...</VibeUploadList>

Props

PropTypeDefaultDescription
unboundedbooleanfalseRemove max-height constraint

VibeUploadItem

Individual file row with thumbnail, progress bar, status icons, and actions.

vue
<VibeUploadItem :file="uploadFile" @remove="removeFile" @retry="retryFile" @cancel="cancelFile" />

<!-- Without preview -->
<VibeUploadItem :file="f" :show-preview="false" />

<!-- Non-removable -->
<VibeUploadItem :file="f" :removable="false" />

<!-- Custom thumbnail -->
<VibeUploadItem :file="f">
  <template #thumb="{ file }">
    <MyCustomThumb :file="file" />
  </template>
</VibeUploadItem>

Props

PropTypeDefaultDescription
fileUploadFilerequiredThe upload file object
showPreviewbooleantrueShow thumbnail for images
removablebooleantrueShow remove/cancel button

Events

EventPayloadDescription
removestring (id)Remove file
retrystring (id)Retry failed upload
cancelstring (id)Cancel in-progress upload

Slots

SlotPropsDescription
thumb{ file }Custom thumbnail
file-icon{ file }Icon for non-image files
status{ status }Custom status indicator

VibeUploadIndicator

Compact status button showing upload progress — useful in toolbars or headers.

vue
<VibeUploadIndicator
  :progress="progress"
  :active="stats.uploading"
  :total="stats.total"
  :complete="isComplete"
  :error="hasErrors"
  @click="toggleUploadPanel"
/>

<!-- Custom label -->
<VibeUploadIndicator :progress="80" :active="2" :total="5" label="Uploading assets…" />

Props

PropTypeDefaultDescription
progressnumberrequiredOverall progress 0–100
activenumberrequiredActive upload count
totalnumberrequiredTotal file count
completebooleanfalseAll complete
errorbooleanfalseAny errors
labelstringautoLabel text override

Composable

useUploadQueue(options)

The core upload engine — manages the queue, concurrency, validation, retry, and cancellation.

ts
const queue = useUploadQueue({
  upload: async (file, { onProgress, signal }) => {
    // Your upload implementation
    // Call onProgress(0–100) for progress updates
    // Respect signal for cancellation
    return serverResponse; // → stored as file.meta
  },
  concurrency: 3,
  autoUpload: true,
  maxRetries: 2,
  previews: true,
  validation: {
    accept: ["image/*", ".pdf", ".docx"],
    maxFileSize: 10 * 1024 * 1024,
    maxFiles: 20,
  },
  onValidationError: (errors) => {
    /* handle invalid files */
  },
  onFileComplete: (file) => {
    /* individual file done */
  },
  onFileError: (file) => {
    /* individual file failed */
  },
  onQueueComplete: (files) => {
    /* all files done */
  },
});

Options

OptionTypeDefaultDescription
uploadUploadFn<TMeta>requiredUpload implementation
concurrencynumber3Max parallel uploads
autoUploadbooleantrueStart immediately when files added
maxRetriesnumber0Auto-retry count on failure
previewsbooleantrueGenerate image preview URLs
validationUploadValidationValidation rules
onValidationError(errors) => voidValidation failure callback
onFileComplete(file) => voidPer-file completion
onFileError(file) => voidPer-file error
onQueueComplete(files) => voidAll files done

Returns

PropertyTypeDescription
filesRef<UploadFile[]>All files in the queue
addFiles(files) => voidAdd files (validates first)
removeFile(id) => voidRemove file (cancels if active)
retryFile(id) => voidRetry a failed file
cancelFile(id) => voidCancel active upload (aborts request)
startAll() => voidStart all queued (when autoUpload is false)
cancelAll() => voidCancel all active uploads
clearCompleted() => voidRemove completed files
clearAll() => voidRemove all files, cancel active
progressComputedRef<number>Overall progress 0–100
isUploadingComputedRef<boolean>Any uploads active
isCompleteComputedRef<boolean>All files done
hasErrorsComputedRef<boolean>Any errors
statsComputedRef<UploadStats>Aggregate counts by status

Upload File Lifecycle

queued → pending → uploading → complete
                            ↘ error → (retry) → queued
                            ↘ cancelled (via abort)

Validation

Runs before files enter the queue. Three checks in order:

  1. Count — total files vs maxFiles
  2. Type — MIME wildcards (image/*), exact MIME, or extensions (.pdf)
  3. Size — file bytes vs maxFileSize

Invalid files are reported via onValidationError and never enter the queue.

Dependencies

PackagePurpose
@vibe-labs/design-components-uploadsCSS tokens + generated styles

Build

bash
npm run build

Built with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.