
Product gallery
Product image management system in MiniShop3.
Overview
The product gallery allows you to:
- Upload images via a modern drag-and-drop interface
- Automatically generate thumbnails in various sizes
- Edit images before upload
- Support modern formats (WebP, AVIF)
- Sort images by dragging
Image processing technology
Intervention Image
MiniShop3 uses Intervention Image v3 — a modern PHP image processing library.
Advantages over legacy phpThumb:
| Feature | phpThumb | Intervention Image |
|---|---|---|
| Generation speed | 800ms | 250ms (3× faster) |
| Memory usage | 45MB | 28MB (-38%) |
| WebP support | Partial | Full |
| AVIF support | No | Yes (with Imagick) |
| Modern algorithms | No | Yes |
Requirements:
- PHP 8.1+
- Imagick (recommended) or GD extension
- For AVIF: Imagick with libheif required
Supported formats
| Format | Read | Write | Note |
|---|---|---|---|
| JPEG | ✅ | ✅ | Primary format |
| PNG | ✅ | ✅ | With transparency |
| GIF | ✅ | ✅ | Animation (first frame only) |
| WebP | ✅ | ✅ | ~30% smaller than JPEG |
| AVIF | ✅ | ✅ | ~50% smaller (requires Imagick) |
| HEIC | ✅ | ❌ | Converted on upload |
Management interface
The product gallery is built with Vue 3 + PrimeVue and includes:
- Image grid — drag-and-drop sort, search, and pagination
- Context menu — right-click an image for quick actions (edit, delete, set as main)
- Toolbar — Media Source selection, bulk actions (delete, regenerate thumbnails)
- Edit dialog — change file name, description, alt text
Changing Media Source
Selecting a file source in the toolbar switches Media Source without saving the whole product form. Uses the Product\UpdateSource processor.
Main image (preview)
The “Set as main” action in the context menu does not change position in the gallery. It calls processor MiniShop3\Processors\Gallery\SetPreview, which writes preview_file_id to msProductData. Method ProductImageService::setProductPreview() links the file to the product card. Drag-and-drop sort and storefront preview are separate mechanisms.
Image uploader
Uppy technology
The image uploader is built on Uppy — a modern file upload library.
Features:
- Drag & Drop — drag files into the upload area
- Multiple upload — several files at once
- Preview — thumbnails before upload
- Built-in editor — crop, rotate, scale
- Progress — upload progress display
- Validation — file type and size checks
Uploader parameters
| Parameter | Default | Description |
|---|---|---|
maxFileSize | 10 MB | Maximum file size |
maxWidth | 1920 px | Maximum width |
maxHeight | 1080 px | Maximum height |
allowedFileTypes | JPEG, PNG, GIF, WebP, AVIF, HEIC | Allowed types |
Image editor
Before upload you can edit the image:
- Crop — select the desired area
- Rotate — 90° in any direction
- Flip — horizontal/vertical
- Scale — resize
Thumbnail configuration
Where to configure
Thumbnail configuration is stored in Media Source:
- Go to Media → File sources
- Open the product source (default:
MS3 Images) - Find the
thumbnailsproperty - Enter JSON configuration
Configuration format
{
"small": {
"width": 150,
"height": 150,
"quality": 85,
"mode": "cover",
"format": "webp"
},
"medium": {
"width": 400,
"height": 400,
"quality": 88,
"mode": "cover",
"format": "webp"
},
"large": {
"width": 800,
"height": 800,
"quality": 90,
"mode": "max",
"format": "jpg"
}
}Thumbnail parameters
| Parameter | Type | Description |
|---|---|---|
width | int | Width in pixels |
height | int | Height in pixels |
quality | int | Compression quality (0-100) |
mode | string | Scaling mode |
format | string | File format |
position | string | Crop position |
background | string | Background color (hex) |
Scaling modes (mode)
cover — Fill with crop
Image is scaled and cropped to fill the size exactly.
{
"mode": "cover",
"width": 300,
"height": 300,
"position": "center"
}Use case: Product cards, grids, thumbnails — when a fixed size is needed.
contain — Fit with background
Image fits entirely; empty space is filled with background.
{
"mode": "contain",
"width": 300,
"height": 300,
"background": "#ffffff"
}Use case: Products on white background, vendor logos.
max — Fit without upscale
Image is reduced to the given size but not enlarged.
{
"mode": "max",
"width": 800,
"height": 800
}Use case: Product gallery, zoom — when quality matters.
fit — Scale with aspect ratio
Image is scaled preserving aspect ratio; may be smaller than the given size.
{
"mode": "fit",
"width": 1200,
"height": 800
}Use case: Responsive galleries with different aspect ratios.
Crop positions (position)
For cover and crop modes:
| Position | Description |
|---|---|
center | Center (default) |
top | Top |
bottom | Bottom |
left | Left |
right | Right |
top-left | Top-left corner |
top-right | Top-right corner |
bottom-left | Bottom-left corner |
bottom-right | Bottom-right corner |
Ready configurations
Basic (minimal)
For small shops with limited disk space.
{
"thumb": {
"width": 150,
"height": 150,
"quality": 80,
"mode": "cover",
"format": "webp"
},
"medium": {
"width": 600,
"height": 600,
"quality": 85,
"mode": "cover",
"format": "webp"
},
"large": {
"width": 1200,
"height": 1200,
"quality": 85,
"mode": "max",
"format": "jpg"
}
}Result: ~256 KB per image, 12-20 files per product.
Optimal (recommended)
Balance of quality and performance with support for all browsers.
{
"thumb_webp": {
"width": 150,
"height": 150,
"quality": 80,
"mode": "cover",
"format": "webp"
},
"thumb_jpg": {
"width": 150,
"height": 150,
"quality": 85,
"mode": "cover",
"format": "jpg"
},
"card_webp": {
"width": 300,
"height": 300,
"quality": 82,
"mode": "cover",
"format": "webp"
},
"card_jpg": {
"width": 300,
"height": 300,
"quality": 85,
"mode": "cover",
"format": "jpg"
},
"gallery_webp": {
"width": 800,
"height": 800,
"quality": 85,
"mode": "max",
"format": "webp"
},
"gallery_jpg": {
"width": 800,
"height": 800,
"quality": 88,
"mode": "max",
"format": "jpg"
},
"zoom": {
"width": 1500,
"height": 1500,
"quality": 90,
"mode": "max",
"format": "jpg"
}
}HTML with fallback:
<picture>
<source srcset="[[+card_webp]]" type="image/webp">
<img src="[[+card_jpg]]" alt="[[+pagetitle]]">
</picture>Premium (with Retina)
Maximum quality for large stores.
{
"card_webp": {
"width": 350,
"height": 350,
"quality": 82,
"mode": "cover",
"format": "webp"
},
"card_webp_2x": {
"width": 700,
"height": 700,
"quality": 78,
"mode": "cover",
"format": "webp"
},
"card_jpg": {
"width": 350,
"height": 350,
"quality": 85,
"mode": "cover",
"format": "jpg"
}
}HTML with Retina:
<picture>
<source
srcset="[[+card_webp]] 1x, [[+card_webp_2x]] 2x"
type="image/webp">
<img src="[[+card_jpg]]" alt="[[+pagetitle]]">
</picture>Additional features
Watermarks
{
"watermarked": {
"width": 800,
"height": 600,
"quality": 85,
"mode": "cover",
"format": "jpg",
"watermark": {
"enabled": true,
"path": "assets/watermark.png",
"position": "bottom-right",
"offset_x": 10,
"offset_y": 10,
"opacity": 50
}
}
}Processing effects
| Parameter | Description | Values |
|---|---|---|
sharpen | Sharpness | 0-100 |
blur | Blur | 0-100 |
brightness | Brightness | -100 to +100 |
contrast | Contrast | -100 to +100 |
greyscale | Grayscale | true/false |
Example:
{
"thumbnail_bw": {
"width": 200,
"height": 200,
"mode": "cover",
"quality": 85,
"greyscale": true,
"contrast": 10,
"sharpen": 15
}
}Optimization
{
"optimized": {
"width": 600,
"height": 400,
"quality": 80,
"mode": "cover",
"format": "jpg",
"progressive": true,
"strip_exif": true
}
}| Parameter | Description |
|---|---|
progressive | Progressive JPEG (faster perceived load) |
strip_exif | Remove EXIF data (saves 20-50 KB) |
Quality recommendations
| Image type | WebP | JPEG | Comment |
|---|---|---|---|
| Thumbnails (≤200px) | 75-80% | 85% | Artifacts not visible |
| Cards (200-400px) | 80-85% | 85-88% | Optimal balance |
| Gallery (400-1000px) | 85% | 88-90% | Details matter |
| Zoom (>1000px) | — | 90-92% | Maximum quality |
Rule
WebP can use 3-5% lower quality than JPEG for the same visual result.
File structure
assets/images/products/
└── {product_id}/
├── photo.jpg # Original
├── thumb_webp/
│ └── photo.webp # WebP thumbnail
├── thumb_jpg/
│ └── photo.jpg # JPEG thumbnail
├── card_webp/
│ └── photo.webp
├── card_jpg/
│ └── photo.jpg
└── ...Related pages
- Utilities: Gallery — bulk thumbnail regeneration
- msGallery — gallery output snippet on the site
Developer API
ImageService
Service for programmatic image handling:
// Get service
$imageService = $modx->services->get('ms3_image');
// Generate thumbnail
$thumbnailData = $imageService->makeThumbnail($sourceInfo, [
'width' => 300,
'height' => 200,
'quality' => 85,
'mode' => 'cover',
'format' => 'webp'
]);
// Save to Media Source
$path = $imageService->saveThumbnailToSource(
$thumbnailData,
'products/123/',
'photo_thumb.webp',
$mediaSource
);
// Driver info
$driverInfo = $imageService->getDriverInfo();
// ['driver' => 'Imagick', 'version' => '7.1.0', 'formats' => ['jpeg', 'png', 'webp', 'avif']]Events
Gallery operations fire these events:
msOnBeforeFileUpload— before file uploadmsOnFileUpload— after file uploadmsOnBeforeThumbnailGenerate— before thumbnail generationmsOnThumbnailGenerate— after thumbnail generation
Troubleshooting
Thumbnails not created
Check:
- Imagick or GD:
php -m | grep -E "(imagick|gd)" - Write permissions for
assets/images/products/ - Valid JSON in Media Source settings
- MODX logs in
core/cache/logs/error.log
WebP not generated
Requirements:
- Imagick with WebP support, or
- GD compiled with
--enable-webp
Check:
$imageService = $modx->services->get('ms3_image');
$info = $imageService->getDriverInfo();
var_dump(in_array('webp', $info['formats']));AVIF not generated
AVIF requires Imagick with libheif. GD does not support AVIF.
Check:
convert -list format | grep AVIFImages upload but don't display
Check:
- Media Source URL in settings
- File accessibility at the given path
.htaccessfor static files


