Skip to main content

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

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)
    }
  }
}

Configuration Options

bucket
string
default:"imagenes"
Supabase Storage bucket name
folder
string
default:""
Optional folder path within the bucket (e.g., ‘reportes’, ‘noticias’)
maxSizeMB
number
default:"5"
Maximum file size in megabytes (default: 5MB)
allowedTypes
string[]
Array of allowed MIME types for image validation

Return Values

uploading
Ref<boolean>
Reactive boolean indicating if an upload is in progress
error
Ref<string | null>
Reactive error message, null if no error
imageUrl
Ref<string>
Reactive string containing the public URL of the last uploaded image
uploadImage
(file: File) => Promise<UploadResult>
Function to upload an image file to Supabase StorageParameters:
  • 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 StorageParameters:
  • imageUrl: Public URL of the image to delete
Returns: Promise resolving to:
{
  success: boolean
  error?: string    // Error message if failed
}
reset
() => void
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

Build docs developers (and LLMs) love