Usage

Overview

The core composable for managing file uploads in your Nuxt application.

useUploadKit

The useUploadKit composable is the heart of Nuxt Upload Kit. It provides a reactive, feature-rich API for managing file uploads.

Basic Usage

<script setup lang="ts">
const uploader = useUploadKit()

const handleFileSelect = async (event: Event) => {
  const input = event.target as HTMLInputElement
  if (input.files) {
    await uploader.addFiles(Array.from(input.files))
  }
}

uploader.onUpload(async (file, onProgress) => {
  // Your upload logic here
  const response = await fetch("/api/upload", {
    method: "POST",
    body: file.data,
  })
  return await response.json()
})
</script>

<template>
  <input type="file" multiple @change="handleFileSelect" />
  <button @click="uploader.upload()">Upload</button>
</template>

Return Value

The composable returns an object with the following properties and methods:

State

PropertyTypeDescription
filesReadonly<Ref<UploadFile[]>>Reactive array of all files
totalProgressComputedRef<number>Overall upload progress (0-100)
statusRef<UploadStatus>Current upload status ('waiting' or 'uploading')
isReadyReadonly<Ref<boolean>>true when initialization is complete

Core Methods

addFile(file: File): Promise<UploadFile>

Add a single file. Runs validation and preprocessing automatically.

const file = await uploader.addFile(fileFromInput)

addFiles(files: File[]): Promise<UploadFile[]>

Add multiple files. Returns successfully added files (failed validations are filtered out).

const addedFiles = await uploader.addFiles(Array.from(input.files))

removeFile(fileId: string, options?): Promise<void>

Remove a file by ID. Optionally control whether to delete from remote storage.

Options:

OptionTypeDefaultDescription
deleteFromStorage"always" | "never" | "local-only""always"Controls storage deletion behavior

deleteFromStorage values:

  • "always" - Always delete from storage if the file has a remoteUrl
  • "never" - Never delete from storage, only remove from local state (useful for forms that reference shared files)
  • "local-only" - Only delete files uploaded in this session (source === "local"), preserving files loaded via initializeExistingFiles
// Default: removes from local state AND deletes from storage
await uploader.removeFile(file.id)

// Only remove from local state, keep in storage
await uploader.removeFile(file.id, { deleteFromStorage: "never" })

// Delete from storage only if this form uploaded it
await uploader.removeFile(file.id, { deleteFromStorage: "local-only" })

removeFiles(fileIds: string[]): UploadFile[]

Remove multiple files at once.

const removed = uploader.removeFiles(["file1", "file2"])

clearFiles(): UploadFile[]

Remove all files.

const allRemoved = uploader.clearFiles()

upload(): Promise<void>

Start uploading all files with status 'waiting'.

await uploader.upload()

reset(): void

Clear all files and reset state.

uploader.reset()

File Access Methods

getFile(fileId: string): UploadFile

Get a file by ID. Throws if not found.

const file = uploader.getFile("file-id")

getFileData(fileId: string): Promise<Blob>

Get the file's binary data. For remote files, fetches from the URL.

For large files (>100MB), this loads the entire file into memory. Consider using getFileURL() or getFileStream() instead.
const blob = await uploader.getFileData(file.id)

getFileURL(fileId: string): Promise<string>

Get a URL for the file. For local files, creates an object URL. For remote files, returns the remote URL.

const url = await uploader.getFileURL(file.id)
// Use for <img src> or <video src>

getFileStream(fileId: string): Promise<ReadableStream<Uint8Array>>

Get a stream for processing large files without loading into memory.

const stream = await uploader.getFileStream(file.id)
const reader = stream.getReader()

while (true) {
  const { done, value } = await reader.read()
  if (done) break
  processChunk(value)
}

File Manipulation

replaceFileData(fileId: string, newData: Blob, newName?: string, shouldAutoUpload?: boolean): Promise<UploadFile>

Replace a file's content (e.g., after cropping). Re-runs preprocessing to regenerate thumbnails.

// After user crops an image
const croppedBlob = await cropImage(originalBlob)
await uploader.replaceFileData(file.id, croppedBlob, "cropped-image.jpg")

updateFile(fileId: string, updates: Partial<UploadFile>): void

Update file properties directly.

uploader.updateFile(file.id, {
  meta: { ...file.meta, customData: "value" },
})

reorderFile(oldIndex: number, newIndex: number): void

Move a file to a different position in the list.

uploader.reorderFile(0, 2) // Move first file to third position

Initialization

initializeExistingFiles(files: InitialFileInput[]): Promise<void>

Load existing files (e.g., from a database) into the uploader. Replaces the current file list.

// Load previously uploaded files
await uploader.initializeExistingFiles([{ storageKey: "uploads/file-1.jpg" }, { storageKey: "uploads/file-2.png" }])

appendExistingFiles(files: InitialFileInput[]): Promise<UploadFile[]>

Append pre-existing remote files without replacing current files. Returns the files that were actually added.

  • Skips files already present (matched by storageKey)
  • Respects maxFiles limit
  • Emits file:added for each appended file
// User picks files from a media library — inject them into the upload manager
const added = await uploader.appendExistingFiles([
  { storageKey: "library/photo-1.jpg" },
  { storageKey: "library/photo-2.jpg" },
])

console.log(`${added.length} files added`)

This is useful when files come from multiple sources (local picker, media library, external integrations) and you want all files managed as first-class citizens in a single files ref.

Plugins

addPlugin(plugin: Plugin): void

Add a plugin at runtime.

import { PluginImageCompressor } from "nuxt-upload-kit"

uploader.addPlugin(
  PluginImageCompressor({
    quality: 0.8,
  }),
)

File Object

Each file in the files array has the following structure:

interface UploadFile {
  id: string // Unique identifier
  name: string // Original filename
  size: number // Size in bytes
  mimeType: string // MIME type
  status: FileStatus // 'waiting' | 'preprocessing' | 'uploading' | 'complete' | 'error'
  source: FileSource // 'local' | 'storage' | etc.
  progress: {
    percentage: number // 0-100
  }
  preview?: string // Thumbnail/preview URL
  remoteUrl?: string // URL after upload
  error?: FileError // Error details if failed
  uploadResult?: any // Result from upload function
  meta: Record<string, unknown> // Custom metadata
  data: File | Blob | null // File data (null for remote files)
}

Complete Example

<script setup lang="ts">
import { PluginAzureDataLake } from "nuxt-upload-kit"

const uploader = useUploadKit({
  storage: PluginAzureDataLake({
    sasURL: "https://storage.blob.core.windows.net/container?sv=...",
    path: "uploads",
  }),
  maxFiles: 10,
  maxFileSize: 50 * 1024 * 1024,
  allowedFileTypes: ["image/*"],
  thumbnails: true,
  imageCompression: { quality: 0.85 },
})

// Listen to events
uploader.on("file:added", (file) => {
  console.log("File added:", file.name)
})

uploader.on("upload:complete", (files) => {
  console.log("All uploads complete:", files.length, "files")
})

uploader.on("file:error", ({ file, error }) => {
  console.error("Upload failed:", file.name, error.message)
})

const handleDrop = async (event: DragEvent) => {
  event.preventDefault()
  const files = Array.from(event.dataTransfer?.files || [])
  await uploader.addFiles(files)
}
</script>

<template>
  <div @drop="handleDrop" @dragover.prevent class="dropzone">
    <p>Drop files here or</p>
    <input type="file" multiple accept="image/*" @change="(e) => uploader.addFiles(Array.from(e.target.files))" />
  </div>

  <div class="file-list">
    <div v-for="file in uploader.files" :key="file.id" class="file-item">
      <img v-if="file.preview" :src="file.preview" class="thumbnail" />

      <div class="file-info">
        <span class="name">{{ file.name }}</span>
        <span class="status">{{ file.status }}</span>
      </div>

      <progress v-if="file.status === 'uploading'" :value="file.progress.percentage" max="100" />

      <button @click="uploader.removeFile(file.id)">Remove</button>
    </div>
  </div>

  <div class="actions">
    <span>Total progress: {{ uploader.totalProgress }}%</span>
    <button @click="uploader.upload()">Upload All</button>
    <button @click="uploader.clearFiles()">Clear All</button>
  </div>
</template>
Copyright © 2026