Developer Docs

Media & Imaging - Integration Guide

Overview

The Media & Imaging system handles all image uploads for Malets โ€” product photos, service images, banners, and more. It features progressive loading with blur-up placeholders, direct-to-cloud uploads via presigned URLs, and optional AI-powered background removal.


Quick Start

Uploading Images

The MediaUploader component is used throughout the management interface (product creation, service editing, blog posts, etc.):

  1. Click the "+ Add Media" button or drag and drop files onto the upload area
  2. Watch the circular progress ring as the file uploads directly to cloud storage
  3. After upload, the server processes the image (generates thumbnails, blur placeholder)
  4. The image appears with a smooth blur-to-sharp transition

Supported Formats

Format Extension Notes
JPEG .jpg Best for photos
PNG .png Best for graphics/transparency
WebP .webp Modern, smaller file sizes
GIF .gif Animated images supported
AVIF .avif Next-gen compression
SVG .svg Vector graphics

Size limit: 10 MB per file

AI Background Removal

When uploading product images, enable the "Remove BG" toggle to automatically remove the background:

  1. Toggle the checkbox before uploading
  2. Upload your image normally
  3. The server's AI model processes the image during confirmation
  4. Result: clean product image with transparent background and adaptive shadow

Progressive Image Loading

How It Works

Every image across the Ngwenya platform uses progressive loading for a polished experience:

  1. Instant: A tiny, blurred version (LQIP โ€” Low Quality Image Placeholder) renders immediately
  2. Loading: The full-resolution image loads in the background
  3. Transition: A smooth 400ms cross-fade reveals the sharp image

If no blur placeholder is available, a shimmer animation plays instead.


Image Uploader

Upload Flow (Presigned S3)

The upload process is secure and efficient โ€” files go directly to cloud storage without passing through the app server:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     1. requestUploadUrl      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Browser โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚  Media   โ”‚
โ”‚         โ”‚ โ—€โ”€โ”€โ”€โ”€ {uploadUrl, fileId} โ”€โ”€  โ”‚ Service  โ”‚
โ”‚         โ”‚                               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚         โ”‚     2. PUT file
โ”‚         โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         โ”‚ โ—€โ”€โ”€โ”€โ”€ 200 OK โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€   โ”‚    S3    โ”‚
โ”‚         โ”‚                               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚         โ”‚     3. confirmUpload(fileId)  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚  Media   โ”‚
โ”‚         โ”‚ โ—€โ”€โ”€โ”€โ”€ MediaFile โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€   โ”‚ Service  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

MediaUploader Component (Frontend)

<script>
	import MediaUploader from '$lib/components/manage/MediaUploader.svelte';
	let images = [];
</script>

<MediaUploader
	bind:images
	label="Product Images"
	maxImages={10}
	maletId="your-malet-id"
	allowBackgroundRemoval={true}
	on:change={(e) => console.log('Updated:', e.detail)}
/>

GraphQL Operations

requestUploadUrl (Mutation)

Request a presigned S3 upload URL:

mutation RequestUploadUrl($input: RequestUploadUrlInput!) {
	requestUploadUrl(input: $input) {
		uploadUrl # Presigned PUT URL (expires in 1 hour)
		fileId # Unique ID for confirmation step
		expiresAt # ISO timestamp
	}
}

confirmUpload (Mutation)

Confirm the upload and trigger processing:

mutation ConfirmUpload($fileId: String!) {
	confirmUpload(fileId: $fileId) {
		id
		url # Public CDN URL
		blurDataUrl # Base64 LQIP (for ProgressiveImage)
		thumbnails {
			label
			url
			width
			height
		}
		metadata {
			width
			height
			format
			size
		}
		status # PENDING โ†’ COMPLETE
	}
}

Integration Checklist

Progressive Image Loading

  • Render ProgressiveImage with blurDataUrl โ€” verify blur placeholder visible
  • Wait for image to load โ€” verify smooth cross-fade transition
  • Render without blurDataUrl โ€” verify shimmer animation plays
  • Test with broken src โ€” verify error placeholder appears

Image Uploader

  • Click "+ Add Media" โ€” verify file dialog opens
  • Select a valid image โ€” verify progress ring appears
  • After processing โ€” verify image thumbnail appears in the grid
  • Drag and drop files โ€” verify drop zone highlight and upload
  • Reach maxImages limit โ€” verify "+ Add Media" button disappears

AI Background Removal

  • Enable "Remove BG" toggle before uploading
  • Upload a product photo โ€” verify processing takes slightly longer
  • Verify result has transparent/clean background

Environment Variables

Variable Description
STORAGE_PROVIDER s3 or local
UPLOAD_DIR Local directory for file uploads (if provider is local)
LOCAL_STORAGE_URL Base URL for serving local files
S3_ENDPOINT Custom endpoint for S3-compatible storage (e.g., R2, MinIO)
S3_REGION S3 bucket region
S3_BUCKET Target S3 bucket name
S3_ACCESS_KEY AWS Access Key ID
S3_SECRET_KEY AWS Secret Access Key
S3_PUBLIC_URL Public CDN URL for serving S3 objects
MAX_FILE_SIZE Maximum allowed file size in bytes
ALLOWED_MIMETYPES Comma-separated list of allowed MIME types