Overview
useImageUpload is a Vue composable that handles image upload and deletion in Supabase Storage. It provides validation, progress tracking, and automatic URL generation with support for custom storage buckets and size limits.
Import
import { useImageUpload } from '@/composables/useImageUpload'
Usage
Basic Upload
Custom Configuration
With Delete
Complete Form Example
import { useImageUpload } from '@/composables/useImageUpload'
import { ref } from 'vue'
// Initialize with default options
const { uploading , error , imageUrl , uploadImage } = useImageUpload ()
const handleFileSelect = async ( event : Event ) => {
const target = event . target as HTMLInputElement
const file = target . files ?.[ 0 ]
if ( file ) {
const result = await uploadImage ( file )
if ( result . success ) {
console . log ( 'Image uploaded:' , result . url )
} else {
console . error ( 'Upload failed:' , result . error )
}
}
}
import { useImageUpload } from '@/composables/useImageUpload'
// Configure bucket, folder, size limit, and allowed types
const { uploading , error , imageUrl , uploadImage } = useImageUpload ({
bucket: 'imagenes' ,
folder: 'reportes' ,
maxSizeMB: 10 ,
allowedTypes: [ 'image/jpeg' , 'image/png' , 'image/webp' ]
})
const handleUpload = async ( file : File ) => {
const { success , url , error } = await uploadImage ( file )
if ( success && url ) {
// Save URL to database
await saveImageUrl ( url )
}
}
import { useImageUpload } from '@/composables/useImageUpload'
import { ref } from 'vue'
const currentImageUrl = ref < string >( '' )
const { uploading , error , uploadImage , deleteImage } = useImageUpload ()
// Upload new image
const handleUpload = async ( file : File ) => {
// Delete old image if exists
if ( currentImageUrl . value ) {
await deleteImage ( currentImageUrl . value )
}
// Upload new image
const result = await uploadImage ( file )
if ( result . success && result . url ) {
currentImageUrl . value = result . url
}
}
// Delete image
const handleDelete = async () => {
if ( currentImageUrl . value ) {
const result = await deleteImage ( currentImageUrl . value )
if ( result . success ) {
currentImageUrl . value = ''
}
}
}
< template >
< div >
< input
type = "file"
@change = "handleFileSelect"
accept = "image/jpeg,image/png,image/webp"
/>
< div v-if = "uploading" class = "text-blue-600" >
Uploading image...
</ div >
< div v-if = "error" class = "text-red-600" >
{{ error }}
</ div >
< img v-if = "imageUrl" :src = "imageUrl" alt = "Uploaded image" />
</ div >
</ template >
< script setup lang = "ts" >
import { useImageUpload } from '@/composables/useImageUpload'
const { uploading , error , imageUrl , uploadImage , reset } = useImageUpload ({
bucket: 'imagenes' ,
folder: 'noticias' ,
maxSizeMB: 5
})
const handleFileSelect = async ( event : Event ) => {
reset () // Clear previous state
const target = event . target as HTMLInputElement
const file = target . files ?.[ 0 ]
if ( file ) {
await uploadImage ( file )
}
}
</ script >
Configuration Options
Supabase Storage bucket name
Optional folder path within the bucket (e.g., ‘reportes’, ‘noticias’)
Maximum file size in megabytes (default: 5MB)
Array of allowed MIME types for image validation
Return Values
Reactive boolean indicating if an upload is in progress
Reactive error message, null if no error
Reactive string containing the public URL of the last uploaded image
uploadImage
(file: File) => Promise<UploadResult>
Function to upload an image file to Supabase Storage Parameters:
file: File object from file input
Returns: Promise resolving to:{
success : boolean
url ?: string // Public URL if successful
error ?: string // Error message if failed
}
deleteImage
(imageUrl: string) => Promise<DeleteResult>
Function to delete an image from Supabase Storage Parameters:
imageUrl: Public URL of the image to delete
Returns: Promise resolving to:{
success : boolean
error ?: string // Error message if failed
}
Function to reset all reactive state (uploading, error, imageUrl) to initial values
Validation
File Type Validation
The composable validates file types against the allowedTypes configuration:
// Default allowed types
const allowedTypes = [ 'image/jpeg' , 'image/jpg' , 'image/png' , 'image/webp' ]
// Custom types
const { uploadImage } = useImageUpload ({
allowedTypes: [ 'image/png' , 'image/webp' ] // Only PNG and WebP
})
If an invalid type is provided, the upload fails with an error message listing the allowed formats.
File Size Validation
The composable validates file size against the maxSizeMB configuration:
// Default 5MB limit
const { uploadImage } = useImageUpload ()
// Custom 10MB limit
const { uploadImage } = useImageUpload ({ maxSizeMB: 10 })
If the file exceeds the limit, the upload fails with an error message.
File Naming
Uploaded files are automatically renamed with a unique name:
const fileName = ` ${ Date . now () } _ ${ Math . random (). toString ( 36 ). substring ( 7 ) } . ${ fileExt } `
// Example: 1709876543210_k5j3h2.jpg
This prevents filename conflicts and ensures unique file paths in storage.
Storage Path Structure
Images are stored with the following path structure:
bucket/
folder/
timestamp_randomhash.ext
Examples:
imagenes/reportes/1709876543210_k5j3h2.jpg
imagenes/noticias/1709876543211_a7b8c9.png
imagenes/1709876543212_x1y2z3.webp (no folder)
Error Handling
const result = await uploadImage ( file )
if ( ! result . success ) {
// error.value = "Formato no válido. Solo se permiten: JPEG, JPG, PNG, WEBP"
console . error ( result . error )
}
const result = await uploadImage ( largeFile )
if ( ! result . success ) {
// error.value = "La imagen es muy grande. El tamaño máximo es 5MB"
console . error ( result . error )
}
const result = await uploadImage ( file )
if ( ! result . success ) {
// Network error, permission denied, etc.
console . error ( 'Upload error:' , result . error )
}
const result = await deleteImage ( imageUrl )
if ( ! result . success ) {
// Image not found, permission denied, etc.
console . error ( 'Delete error:' , result . error )
}
Integration with Features
This composable is used throughout the Portal Ciudadano Manta application:
Reports System - Upload images for problem reports (features/reports)
News Management - Upload featured images for news articles (features/news)
Survey System - Upload images for survey questions (features/surveys)
Reports System Learn how images are used in citizen reports
News Management Learn how images are used in news articles
Storage Configuration
Make sure your Supabase Storage bucket is properly configured:
-- Create imagenes bucket if not exists
INSERT INTO storage . buckets (id, name , public)
VALUES ( 'imagenes' , 'imagenes' , true);
-- Set up storage policies (see deployment/storage-policies)
Storage Policies Configure Row Level Security policies for image uploads