Magento 2: Get Gallery Images Programmatically (5 Methods)
[Updated: March 13, 2026]
Five ways to retrieve product gallery images in Magento 2. PHP service contracts, the Media Gallery API, Image Helper, REST API, and GraphQL. Each method with working code examples.
Key Takeaways
- Magento 2 offers five distinct methods to retrieve product gallery images: DI with ProductRepository, DI with ProductFactory, the Media Gallery Management API, Catalog Image Helper, and GraphQL.
-
getMediaGalleryEntries()returns structured objects with metadata.getMediaGalleryImages()returns a simpler collection with URLs. - Since Magento 2.4.6,
getMediaGalleryImages()on Factory-loaded products may return empty unless you trigger theGallery\ReadHandlerfirst. Repository-loaded products are not affected. - Hidden and disabled images require explicit filtering through the media gallery collection.
- The REST API endpoint
GET /rest/V1/products/{sku}/mediaprovides external access without PHP code.
What is Magento 2 Get Gallery Image?
Get Gallery Image = Programmatic retrieval of product images from Magento 2's media gallery. Returns file paths, URLs, image roles (base, small, thumbnail), and metadata like alt text and position.
Perfect for: Developers building custom templates, data feeds, product exports, or headless frontends.
Not ideal for: Store admins who need a visual gallery builder (use an extension or the admin media gallery instead).
Every Magento 2 product can hold multiple gallery images with assigned roles. The base_image is the main product photo. The small_image appears in category listings. The thumbnail shows in the cart and cross-sell blocks. The swatch_image drives configurable product selectors.
Retrieving these images programmatically is a core task for Magento storefronts with custom templates, third-party integrations, or headless architectures.
5 Methods to Get Gallery Images (Magento 2.4.6 through 2.4.8)
| Method | Best For | Returns | Magento Version |
|---|---|---|---|
ProductRepositoryInterface + getMediaGalleryEntries() |
Structured data with metadata | MediaGalleryEntry[] objects |
All |
ProductFactory + getMediaGalleryImages() |
Quick URL collection | DataObject collection |
All (2.4.6 fix needed) |
| MediaGalleryManagementInterface | SKU-based retrieval | MediaGalleryEntry[] objects |
All |
| Catalog Image Helper | Frontend URL generation with resizing | Resized image URL string | All |
| GraphQL | Headless / PWA frontends | JSON with URLs + metadata | 2.3+ |
Method 1: ProductRepositoryInterface (Recommended)
The cleanest approach using Magento's service contracts. Returns MediaGalleryEntry objects with full metadata.
<?php
declare(strict_types=1);
namespace Vendor\Module\Model;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;
class GalleryRetriever
{
private ProductRepositoryInterface $productRepository;
private StoreManagerInterface $storeManager;
public function __construct(
ProductRepositoryInterface $productRepository,
StoreManagerInterface $storeManager
) {
$this->productRepository = $productRepository;
$this->storeManager = $storeManager;
}
public function getImagesByProductId(int $productId): array
{
$product = $this->productRepository->getById($productId);
$images = [];
$mediaUrl = $this->storeManager->getStore()
->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA);
foreach ($product->getMediaGalleryEntries() as $entry) {
$images[] = [
'id' => $entry->getId(),
'file' => $entry->getFile(),
'url' => $mediaUrl . 'catalog/product' . $entry->getFile(),
'label' => $entry->getLabel(),
'position' => $entry->getPosition(),
'disabled' => $entry->isDisabled(),
'types' => $entry->getTypes(), // ['image', 'small_image', 'thumbnail']
'media_type'=> $entry->getMediaType(),
];
}
return $images;
}
}
When to use: Most scenarios. This is the Magento-recommended approach via service contracts. Works with both product ID and SKU (getById() vs get($sku)).
Method 2: ProductFactory + getMediaGalleryImages()
A simpler approach that returns a collection with pre-built URLs. Useful when you need a quick list without full metadata.
<?php
declare(strict_types=1);
use Magento\Catalog\Model\ProductFactory;
use Magento\Catalog\Model\ResourceModel\Product\Gallery\ReadHandler;
class QuickGalleryLoader
{
private ProductFactory $productFactory;
private ReadHandler $galleryReadHandler;
public function __construct(
ProductFactory $productFactory,
ReadHandler $galleryReadHandler
) {
$this->productFactory = $productFactory;
$this->galleryReadHandler = $galleryReadHandler;
}
public function getImages(int $productId): array
{
$product = $this->productFactory->create()->load($productId);
// Required since Magento 2.4.6+
$this->galleryReadHandler->execute($product);
$images = [];
foreach ($product->getMediaGalleryImages() as $image) {
$images[] = [
'url' => $image->getUrl(),
'file' => $image->getFile(),
'label' => $image->getLabel(),
'position' => $image->getPosition(),
'disabled' => (bool)$image->getDisabled(),
];
}
return $images;
}
}
When to use: Quick retrieval with direct URLs. Less boilerplate than Method 1.
getMediaGalleryImages() vs getMediaGalleryEntries()
These two methods cause the most confusion. Here is the difference:
| Feature | getMediaGalleryEntries() | getMediaGalleryImages() |
|---|---|---|
| Returns | MediaGalleryEntry[] objects |
DataObject collection |
| Available on | Products loaded via Repository | Products loaded via Factory/Model |
| Includes image types | Yes (getTypes()) |
No |
| Includes media type | Yes (image/video) | No |
| Returns full URLs | No (relative paths) | Yes (getUrl()) |
| Includes disabled images | Yes (check isDisabled()) |
No (filtered out) |
| Needs ReadHandler (2.4.6+) | No | Yes |
Rule of thumb: Use getMediaGalleryEntries() when you need metadata, image roles, or disabled images. Use getMediaGalleryImages() when you need URLs fast and don't care about hidden images.
Method 3: Get Images by SKU
When you have the SKU but not the product ID, use ProductAttributeMediaGalleryManagementInterface:
<?php
declare(strict_types=1);
use Magento\Catalog\Api\ProductAttributeMediaGalleryManagementInterface;
use Magento\Framework\Exception\NoSuchEntityException;
class SkuGalleryLoader
{
private ProductAttributeMediaGalleryManagementInterface $galleryManagement;
public function __construct(
ProductAttributeMediaGalleryManagementInterface $galleryManagement
) {
$this->galleryManagement = $galleryManagement;
}
public function getImagesBySku(string $sku): array
{
try {
return $this->galleryManagement->getList($sku);
} catch (NoSuchEntityException $e) {
// SKU does not exist in catalog
return [];
}
}
}
The underlying service class (Magento\Catalog\Model\Product\Gallery\GalleryManagement) throws a NoSuchEntityException if the SKU does not exist. Always handle this in batch operations where SKU lists may contain outdated entries.
This returns the same data structure as getMediaGalleryEntries() but accessed through the dedicated gallery service contract. Useful for batch operations where you iterate over SKU lists.
Method 4: Catalog Image Helper (Frontend URLs)
For frontend templates where you need resized, cached image URLs:
<?php
declare(strict_types=1);
use Magento\Catalog\Helper\Image as ImageHelper;
use Magento\Catalog\Api\ProductRepositoryInterface;
class FrontendImageUrl
{
private ImageHelper $imageHelper;
private ProductRepositoryInterface $productRepository;
public function __construct(
ImageHelper $imageHelper,
ProductRepositoryInterface $productRepository
) {
$this->imageHelper = $imageHelper;
$this->productRepository = $productRepository;
}
public function getResizedUrl(int $productId, string $imageType = 'product_page_image_large'): string
{
$product = $this->productRepository->getById($productId);
return $this->imageHelper
->init($product, $imageType)
->setImageFile($product->getImage())
->getUrl();
}
}
The $imageType parameter maps to image IDs defined in view.xml. Common types: product_page_image_large, product_page_image_medium, category_page_list, product_small_image, cart_page_product_thumbnail.
For a specific gallery image (not just the assigned role image), pass the file path as the third argument to init():
// Get resized URL for a specific gallery entry
foreach ($product->getMediaGalleryEntries() as $entry) {
$url = $this->imageHelper
->init($product, 'product_page_image_medium', $entry->getFile())
->getUrl();
}
This method generates resized and cached images. It is the recommended approach for frontend rendering where you need specific dimensions.
Method 5: GraphQL (Headless / PWA)
For headless frontends and PWA implementations, query the media gallery through GraphQL:
{
products(filter: { sku: { eq: "24-MB01" } }) {
items {
name
sku
media_gallery {
url
label
position
disabled
... on ProductVideo {
video_content {
media_type
video_provider
video_url
video_title
}
}
}
image {
url
label
}
small_image {
url
label
}
thumbnail {
url
label
}
}
}
}
The media_gallery field returns all gallery entries. The image, small_image, and thumbnail fields return the assigned role images. For PWA Studio or custom React/Vue frontends, this is the primary method.
REST API: External Access
Retrieve gallery images without PHP code through the REST API:
GET /rest/V1/products/{sku}/media
Example request:
curl -X GET "https://your-store.com/rest/V1/products/24-MB01/media" \
-H "Authorization: Bearer YOUR_TOKEN"
Response structure:
[
{
"id": 1,
"media_type": "image",
"label": "Product front view",
"position": 1,
"disabled": false,
"types": ["image", "small_image"],
"file": "/m/b/mb01-blue-0.jpg"
}
]
Use this for external integrations, data feeds, or tools that need image data without running inside the Magento codebase.
Getting Hidden and Disabled Images
By default, getMediaGalleryImages() filters out disabled images. To include them:
$product = $this->productRepository->getById($productId);
$allImages = [];
foreach ($product->getMediaGalleryEntries() as $entry) {
$allImages[] = [
'file' => $entry->getFile(),
'disabled' => $entry->isDisabled(),
'label' => $entry->getLabel(),
];
}
// Filter only hidden images
$hiddenImages = array_filter($allImages, fn($img) => $img['disabled']);
Use getMediaGalleryEntries() instead of getMediaGalleryImages() when you need access to disabled entries. This is common for inventory management tools, data exports, and admin-facing modules.
Magento 2.4.6+: Gallery ReadHandler Required
getMediaGalleryImages() returns empty for ProductFactory-loaded products. Fix: call ReadHandler→execute() first. Not affected: ProductRepositoryInterface.
Since Magento 2.4.6, getMediaGalleryImages() on products loaded via ProductFactory::create()->load() may return an empty collection. The root cause: gallery attributes are no longer eager-loaded for Factory/Model-loaded products.
The fix: Inject and call Gallery\ReadHandler before accessing gallery images:
use Magento\Catalog\Model\ResourceModel\Product\Gallery\ReadHandler;
// In your constructor
private ReadHandler $galleryReadHandler;
// Before calling getMediaGalleryImages()
$this->galleryReadHandler->execute($product);
$images = $product->getMediaGalleryImages(); // Now returns data
This affects only ProductFactory-loaded products. When using ProductRepositoryInterface (get() / getById()), the gallery data is already initialized through the service layer. The ReadHandler call is not needed there.
Version compatibility: The ReadHandler call is safe on all Magento 2.x versions. It runs without side effects on pre-2.4.6 installations. If your code must support multiple versions, always include it.
Tested on Magento 2.4.6 through 2.4.8. No further breaking changes to the media gallery handling since 2.4.6.
Image Roles Explained
Magento assigns roles to gallery images. Each role controls where the image appears:
| Role | Admin Label | Where It Shows | Attribute Code |
|---|---|---|---|
| Base Image | Image | Product detail page (main photo) | image |
| Small Image | Small Image | Category listings, search results | small_image |
| Thumbnail | Thumbnail | Cart, cross-sells, related products | thumbnail |
| Swatch Image | Swatch | Configurable product visual swatches (colors, patterns) | swatch_image |
Access a specific role image:
$product = $this->productRepository->getById($productId);
$baseImage = $product->getData('image'); // e.g. /m/b/mb01.jpg
$smallImage = $product->getData('small_image');
$thumbnail = $product->getData('thumbnail');
One image can hold multiple roles. A product with a single photo often has that image assigned to all four roles. The swatch_image role has grown in importance since Magento 2.3+ as visual swatches drive product selection in configurable products. For PWA and headless frontends, swatch images are often the primary visual element in product variant selectors.
Performance Best Practices
Avoid loading full products in loops. If you need gallery images for 50 products on a category page, do not call $productRepository->getById() 50 times. Use a collection with the addMediaGalleryData() method:
$collection = $this->collectionFactory->create();
$collection->addIdFilter($productIds);
$collection->addAttributeToSelect(['image', 'small_image', 'thumbnail']);
$collection->addMediaGalleryData();
foreach ($collection as $product) {
$images = $product->getMediaGalleryImages();
}
The addAttributeToSelect() call limits which EAV attributes Magento loads. For catalogs with 10,000+ products, this saves significant memory compared to loading all attributes.
Use the Image Helper for frontend rendering. It generates cached, resized images. Serving the original 4000x4000px upload wastes bandwidth and kills page speed.
Serve WebP images when possible. WebP delivers 25-34% smaller files than JPEG at comparable quality. Magento 2.4+ supports WebP conversion through the admin image optimization settings.
Configure a CDN for media delivery. Product images are the heaviest assets on most Magento stores. A CDN reduces latency for international visitors and offloads bandwidth from your origin server.
For stores with large catalogs (10,000+ products), pair image optimization with reliable managed Magento hosting for consistent media delivery performance.
FAQ
1. What is the difference between getMediaGalleryImages() and getMediaGalleryEntries()?
getMediaGalleryEntries() returns MediaGalleryEntry objects with full metadata: image types (roles), media type, disabled status, and relative file paths. getMediaGalleryImages() returns a simpler DataObject collection with pre-built URLs but excludes disabled images and role information.
2. Why does getMediaGalleryImages() return empty in Magento 2.4.6+?
Magento 2.4.6 changed how gallery data initializes for products loaded via ProductFactory. Inject Magento\Catalog\Model\ResourceModel\Product\Gallery\ReadHandler and call $readHandler->execute($product) before accessing the gallery. This is not needed when using ProductRepositoryInterface.
3. How do I get product gallery images by SKU instead of ID?
Use ProductAttributeMediaGalleryManagementInterface::getList($sku). It returns MediaGalleryEntry[] objects for the given SKU without loading the full product model. You can also use $productRepository->get($sku) to load by SKU through the repository.
4. How do I get hidden or disabled product images?
Use getMediaGalleryEntries() and check $entry->isDisabled(). The getMediaGalleryImages() method filters out disabled images by default. The REST API also includes disabled images with a disabled: true flag.
5. Where are product images stored in Magento 2?
Image files are stored in pub/media/catalog/product/ with a hashed directory structure (e.g., /m/b/mb01.jpg). Metadata (labels, positions, roles, disabled status) is stored in the catalog_product_entity_media_gallery and catalog_product_entity_media_gallery_value database tables.
6. How do I get gallery images via the REST API?
Send a GET request to /rest/V1/products/{sku}/media with a valid bearer token. The response is a JSON array of gallery entries with id, file path, label, position, disabled status, types (roles), and media type.
7. Can I retrieve video entries from the product gallery?
Yes. Videos are stored as gallery entries with media_type: external-video. Use getMediaGalleryEntries() and filter by $entry->getMediaType() === 'external-video'. The video URL, provider, and title are in the video_content extension attribute. GraphQL supports video data through the ProductVideo fragment.
8. How do I get the full image URL instead of a relative path?
For getMediaGalleryEntries(), concatenate the store media URL with the file path: $storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_MEDIA) . 'catalog/product' . $entry->getFile(). For frontend use, the Image Helper generates cached, resized URLs.
9. What image types are available in Magento 2?
Magento 2 defines four built-in image roles: image (base/main), small_image (category listings), thumbnail (cart/cross-sells), and swatch_image (configurable selectors). Custom image types can be defined in your theme's etc/view.xml file with specific dimensions and aspect ratios.
10. How do I use GraphQL to retrieve media gallery images?
Query the products field with media_gallery { url label position disabled } in the selection set. For videos, use the ... on ProductVideo inline fragment. The image, small_image, and thumbnail fields return the role-assigned images. This is the standard approach for PWA Studio and headless Magento frontends.
11. How do I improve gallery image loading performance?
Use addMediaGalleryData() on product collections instead of loading products one by one. Serve resized images via the Image Helper (not raw uploads). Enable WebP conversion in admin settings. Configure a CDN for media delivery. For category pages with 50+ products, consider lazy loading gallery images below the fold.
Summary
Magento 2 provides five ways to retrieve product gallery images: ProductRepository service contracts, ProductFactory with ReadHandler, the Media Gallery Management API, the Catalog Image Helper, and GraphQL.
For most PHP development work, ProductRepositoryInterface + getMediaGalleryEntries() is the recommended approach. It works across all Magento 2.4.x versions without the 2.4.6+ ReadHandler workaround. For headless frontends, use GraphQL. For external integrations, use the REST API.
Match your method to your use case. Use the Image Helper for frontend rendering. Use the repository for backend logic. Use GraphQL for PWA. Pair your implementation with managed Magento hosting for fast media delivery across all methods.