Advanced
Storage Adapters
Create your own storage adapters for custom cloud providers.
Custom Storage Adapters
Use defineStorageAdapter to create custom storage providers for uploading files to any cloud or self-hosted storage.
Basic Structure
import { defineStorageAdapter } from "nuxt-upload-kit"
interface MyStorageOptions {
apiUrl: string
apiKey: string
}
interface MyStorageResult {
url: string
id: string
}
export const PluginMyStorage = defineStorageAdapter<MyStorageOptions, MyStorageResult>((options) => ({
id: "my-storage",
hooks: {
upload: async (file, context) => {
// Upload implementation
return { url: "...", id: "..." }
},
},
}))
Hook Reference
| Hook | Required | When | Purpose | Return |
|---|---|---|---|---|
upload | Yes | During upload | Send file to storage | { url, ...data } |
getRemoteFile | No | Loading existing | Fetch file metadata | File metadata |
remove | No | Deleting file | Delete from storage | void |
Complete Example
import { defineStorageAdapter } from "nuxt-upload-kit"
interface MyStorageOptions {
apiUrl: string
apiKey: string
}
interface MyStorageResult {
url: string
id: string
etag: string
}
export const PluginMyStorage = defineStorageAdapter<MyStorageOptions, MyStorageResult>((options) => ({
id: "my-storage",
hooks: {
// Required: Upload a file
upload: async (file, context) => {
const formData = new FormData()
formData.append("file", file.data as Blob)
const response = await fetch(options.apiUrl, {
method: "POST",
headers: { "X-API-Key": options.apiKey },
body: formData,
})
const result = await response.json()
// Must return object with 'url' property
return {
url: result.fileUrl,
id: result.fileId,
etag: result.etag,
}
},
// Optional: Get file metadata for existing files
getRemoteFile: async (fileId, context) => {
const response = await fetch(`${options.apiUrl}/${fileId}`, {
headers: { "X-API-Key": options.apiKey },
})
const metadata = await response.json()
return {
size: metadata.size,
mimeType: metadata.contentType,
remoteUrl: metadata.url,
preview: metadata.thumbnailUrl,
}
},
// Optional: Delete a file
remove: async (file, context) => {
// Use storageKey for deletion (set after upload or from initialFiles)
if (!file.storageKey) return
await fetch(`${options.apiUrl}/${file.storageKey}`, {
method: "DELETE",
headers: { "X-API-Key": options.apiKey },
})
},
},
}))
Progress Reporting
Use context.onProgress to report upload progress:
upload: async (file, context) => {
const xhr = new XMLHttpRequest()
return new Promise((resolve, reject) => {
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
context.onProgress(Math.round((e.loaded / e.total) * 100))
}
}
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error("Upload failed"))
}
}
xhr.onerror = () => reject(new Error("Network error"))
const formData = new FormData()
formData.append("file", file.data as Blob)
xhr.open("POST", options.apiUrl)
xhr.setRequestHeader("X-API-Key", options.apiKey)
xhr.send(formData)
})
}
Return Value Requirements
The upload hook must return an object containing at least a url property:
// Minimum required
return { url: "https://storage.example.com/file.jpg" }
// With additional metadata (recommended)
return {
url: "https://storage.example.com/file.jpg",
storageKey: "uploads/user-123/file.jpg", // Full path for retrieval/deletion
etag: "abc123",
bucket: "my-bucket",
}
The returned object becomes available as file.uploadResult after the upload completes.
The storageKey Pattern
The storageKey is the full path used to identify files in storage. It enables:
- Loading existing files via
initialFiles - Deleting files with
removeFile() - Round-trip consistency (upload → store key → retrieve later)
// Upload returns storageKey
upload: async (file, context) => {
const path = `uploads/${options.folder}/${file.id}`
await uploadToStorage(path, file.data)
return {
url: `https://cdn.example.com/${path}`,
storageKey: path, // Save this to your database
}
}
// getRemoteFile receives storageKey
getRemoteFile: async (storageKey, context) => {
const metadata = await getFromStorage(storageKey)
return {
size: metadata.size,
mimeType: metadata.contentType,
remoteUrl: `https://cdn.example.com/${storageKey}`,
}
}
// remove receives file with storageKey
remove: async (file, context) => {
if (!file.storageKey) return // Not uploaded yet
await deleteFromStorage(file.storageKey)
}
Store the
storageKey in your database alongside other file metadata. Pass it back via initialFiles to reload files.Remote File Metadata
The getRemoteFile hook must return an object with these properties:
interface RemoteFileMetadata {
size: number // File size in bytes
mimeType: string // MIME type (e.g., "image/jpeg")
remoteUrl: string // URL to access the file
preview?: string // Optional thumbnail URL
}
Usage
const uploader = useUploadKit({
storage: PluginMyStorage({
apiUrl: "https://api.example.com/files",
apiKey: "your-api-key",
}),
})
// After upload, access the result
uploader.on("upload:complete", (files) => {
files.forEach((file) => {
console.log("File URL:", file.uploadResult.url)
console.log("File ID:", file.uploadResult.id)
})
})
Error Handling
Throw errors to fail the upload:
upload: async (file, context) => {
const response = await fetch(options.apiUrl, {
method: "POST",
body: file.data,
})
if (!response.ok) {
throw new Error(`Upload failed: ${response.statusText}`)
}
return await response.json()
}
The error will be captured and the file's status will be set to error with the error message available in file.error.

