Optimization Guide
Shopify ImageObject Schema for Visual Search — Product Image Structured Data & AI Vision Markup
Google Lens processes over 12 billion visual searches per month. AI shopping agents with vision capabilities — including GPT-4o, Gemini, and Claude — index product images to answer queries like "find me a lamp that looks like this" or "where can I buy the jacket worn in this photo." Shopify's default JSON-LD outputs product images as bare URL strings, discarding alt text, dimensions, primary-image signals, and gallery structure. Upgrading to full ImageObject markup is the single highest-leverage visual-search optimization for Shopify merchants.
"image": "https://cdn.shopify.com/…jpg" with a full ImageObject block: include contentUrl, description (mapped from alt text), width, height, encodingFormat, and representativeOfPage: true on the primary image. Use an array of ImageObject values for multi-image galleries. Map product.images[i].alt to the description field — Shopify's default output silently drops alt text from JSON-LD. Add thumbnail for hover-preview-optimized versions.
Why Product Images Are Invisible to AI Visual Search Without Schema
Visual search AI systems index products in two steps: (1) crawl the image URL to analyze the visual content, and (2) read the structured data on the page to understand what the image represents. Without structured data, a crawler seeing "image": "https://cdn.shopify.com/s/files/1/0001/myproduct.jpg" gets only the image URL — no description, no context about whether it's the primary product shot or a packaging detail, no alt text, no dimensions. The AI must infer everything from the pixel content alone, which is error-prone for product imagery (multiple angles, lifestyle context, similar-looking SKUs).
Full ImageObject markup solves this by providing machine-readable metadata alongside the visual content. Google's systems use representativeOfPage to select which image to show in Shopping tiles. AI shopping agents use description to understand what is shown in each image before fetching it. Image search ranking algorithms use width/height aspect ratio and encodingFormat to match images to query contexts (portrait for apparel, square for products, widescreen for lifestyle).
ImageObject property reference
| Property | Type | Visual search use | Example |
|---|---|---|---|
contentUrl |
URL | Primary crawlable image URL (CDN link) | https://cdn.shopify.com/…/product.jpg |
url |
URL | Canonical page URL where the image lives — use product URL | https://example.com/products/navy-wool-coat |
description |
Text | Alt text — AI semantic understanding of image content | Navy double-breasted wool coat, front view |
caption |
Text | Editorial caption — additional context beyond alt text | Available in navy, camel, and forest green |
representativeOfPage |
Boolean | Designates the primary product image for Shopping tiles | true (primary only) |
width |
QuantitativeValue or Integer | Pixel width — aspect ratio signal for format matching | 1200 |
height |
QuantitativeValue or Integer | Pixel height — aspect ratio signal | 1600 |
encodingFormat |
Text (MIME type) | Image format — WebP preferred for performance signals | image/webp |
thumbnail |
ImageObject | Smaller preview version — used in rich snippets + social cards | 400px version of the same image |
Primary Image Pattern: Single ImageObject on Product
For the simplest case — a product with one primary image — replace the bare string image URL with a full ImageObject. Set representativeOfPage: true to designate it as the page's primary visual. Map the Shopify image alt text to description.
{
"@type": "Product",
"name": "Navy Wool Coat",
"image": "https://cdn.shopify.com/
s/files/1/0001/coat.jpg"
// No alt text
// No dimensions
// No primary signal
// AI infers everything from pixels
}
{
"@type": "Product",
"name": "Navy Wool Coat",
"image": {
"@type": "ImageObject",
"contentUrl": "https://cdn.../coat.jpg",
"description": "Navy wool coat,
front view on white bg",
"representativeOfPage": true,
"width": 1200,
"height": 1600,
"encodingFormat": "image/jpeg"
}
}
Multi-Image Gallery: ImageObject Array
When a product has multiple images (multiple angles, lifestyle shots, detail views), output them as an array under the Product's image property. Set representativeOfPage: true only on the primary image — usually the main white-background hero shot.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Navy Double-Breasted Wool Coat",
"description": "Tailored double-breasted coat in 100% virgin wool. Notched lapels, two front welt pockets, fully lined. Available in sizes XS–3XL.",
"sku": "COAT-NAVY-DB-WOOL",
"brand": { "@type": "Brand", "name": "Archibald & Co." },
"image": [
{
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/s/files/1/0001/coat-front.jpg",
"url": "https://example.com/products/navy-wool-coat",
"description": "Navy double-breasted wool coat, front view, white background — Archibald & Co.",
"representativeOfPage": true,
"width": 1200,
"height": 1600,
"encodingFormat": "image/jpeg",
"thumbnail": {
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/s/files/1/0001/coat-front_400x.jpg",
"width": 400,
"height": 533,
"encodingFormat": "image/jpeg"
}
},
{
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/s/files/1/0001/coat-back.jpg",
"url": "https://example.com/products/navy-wool-coat",
"description": "Navy double-breasted wool coat, back view showing single center vent and half-belt detail",
"representativeOfPage": false,
"width": 1200,
"height": 1600,
"encodingFormat": "image/jpeg"
},
{
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/s/files/1/0001/coat-detail-lapel.jpg",
"url": "https://example.com/products/navy-wool-coat",
"description": "Close-up detail of notched lapel and top button on navy wool coat",
"representativeOfPage": false,
"width": 800,
"height": 800,
"encodingFormat": "image/jpeg"
},
{
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/s/files/1/0001/coat-lifestyle.jpg",
"url": "https://example.com/products/navy-wool-coat",
"description": "Model wearing navy wool coat over cream turtleneck and dark trousers, city street setting",
"caption": "Styled with Archibald & Co. merino turtleneck",
"representativeOfPage": false,
"width": 1200,
"height": 1500,
"encodingFormat": "image/jpeg"
}
]
}
</script>
The caption property on lifestyle images provides additional editorial context — AI agents use it to understand styling relationships between products. For the lifestyle shot above, the caption signals that the coat is styled with a turtleneck, which can influence "complete the look" AI shopping recommendations.
Alt Text in JSON-LD: The Shopify Gap
Shopify's Dawn theme and most popular themes render product JSON-LD from the product.json object, which includes only the image URL — not the alt text you've painstakingly entered in the Shopify admin (Products → Edit → Images → Add alt text). This is a significant gap: Google's image indexing systems and AI vision agents use the description field in ImageObject as the primary text signal for understanding image content in the absence of on-page context.
The fix is straightforward in Liquid: the product.images[i].alt property exposes the admin-entered alt text. Map it to the description field in your ImageObject JSON-LD block. If no alt text has been entered, fall back to the product title concatenated with a positional description ("Product image 2 of 4").
Alt text quality signals for AI visual search
| Alt text quality | Example | AI visual search impact |
|---|---|---|
| Poor | image1.jpg (filename only) |
Zero semantic signal; AI relies entirely on pixel analysis |
| Basic | Navy coat |
Color + product type — minimal for disambiguation |
| Good | Navy double-breasted wool coat, front view |
Color, material, style, angle — strong product-level signal |
| Optimal | Navy double-breasted 100% virgin wool coat by Archibald & Co., front view on white background, sizes XS–3XL |
Includes brand, material composition, size range — AI can match "wool coats XS size" visual queries |
Dawn Liquid Snippet: Full ImageObject Output
Save as snippets/product-image-schema.liquid and render from your product template via {% raw %}{% render 'product-image-schema' %}{% endraw %}. The snippet iterates all product images, outputs a full ImageObject for each, sets representativeOfPage: true on the first, and falls back to a clean description string if no alt text is present.
{% comment %} product-image-schema.liquid — full ImageObject JSON-LD for all product images {% endcomment %}
{% assign primary_image = product.featured_image %}
{% assign all_images = product.images %}
{% if all_images.size > 0 %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"url": "{{ shop.url }}{{ product.url }}",
"image": [
{% for image in all_images %}
{% assign is_primary = false %}
{% if image.id == primary_image.id %}{% assign is_primary = true %}{% endif %}
{% assign img_alt = image.alt %}
{% if img_alt == blank %}
{% assign img_alt = product.title | append: ' — product image ' | append: forloop.index | append: ' of ' | append: all_images.size %}
{% endif %}
{% assign img_url = image | img_url: '1200x' %}
{% assign thumb_url = image | img_url: '400x' %}
{% assign img_format = 'image/jpeg' %}
{% if image.src contains '.webp' %}{% assign img_format = 'image/webp' %}{% endif %}
{% if image.src contains '.png' %}{% assign img_format = 'image/png' %}{% endif %}
{
"@type": "ImageObject",
"contentUrl": "https:{{ img_url }}",
"url": "{{ shop.url }}{{ product.url }}",
"description": {{ img_alt | json }},
"representativeOfPage": {{ is_primary }},
"encodingFormat": {{ img_format | json }},
"thumbnail": {
"@type": "ImageObject",
"contentUrl": "https:{{ thumb_url }}",
"encodingFormat": {{ img_format | json }}
}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}
</script>
{% endif %}
This snippet outputs the image array as a standalone Product JSON-LD block. To merge it with your existing product JSON-LD, extract just the "image": [...] array and splice it into your main product schema block. The standalone approach is safer for most themes as it avoids JSON-LD conflicts.
ImageGallery for Editorial Collections
For editorial lookbook pages or collection pages that feature multiple products in context, use ImageGallery — an ItemList of ImageObject values. This schema type signals to AI that the page is a curated image collection rather than a single product page, enabling AI shopping agents to identify and surface individual products from within the gallery.
{
"@context": "https://schema.org",
"@type": "ImageGallery",
"name": "Spring 2026 Collection — Lookbook",
"description": "Styled product photography for the Archibald & Co. Spring 2026 outerwear collection.",
"url": "https://example.com/collections/spring-2026",
"image": [
{
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/…/lookbook-01.jpg",
"description": "Model in navy wool coat and camel trousers, rooftop setting",
"about": {
"@type": "Product",
"name": "Navy Wool Coat",
"url": "https://example.com/products/navy-wool-coat"
}
},
{
"@type": "ImageObject",
"contentUrl": "https://cdn.shopify.com/…/lookbook-02.jpg",
"description": "Model in camel cashmere overcoat, park setting",
"about": {
"@type": "Product",
"name": "Camel Cashmere Overcoat",
"url": "https://example.com/products/camel-cashmere-overcoat"
}
}
]
}
The about property on each ImageObject in the gallery links the image to the specific product it depicts. AI shopping agents use this to serve "shop the look" recommendations — when a user uploads a photo of a coat they saw in a lookbook, the AI can trace the image back to the product via this structured link.
Shopify Metafield Architecture for Image SEO
| Metafield key | Type | Maps to | Notes |
|---|---|---|---|
seo.image_caption |
Multi-line text | ImageObject.caption (primary image) |
Editorial caption for lifestyle/hero image |
seo.image_license |
URL | ImageObject.license |
Optional: points to usage rights page (for wholesale/press kits) |
seo.primary_image_width |
Integer | ImageObject.width |
Override for width when CDN transformation changes dimensions |
seo.primary_image_height |
Integer | ImageObject.height |
Override for height |
Note: Shopify Liquid's img_url filter applies size transformations (e.g., image | img_url: '1200x'), which changes the rendered image dimensions from the original. If your product images are uploaded at 2000×2000 and you serve them at 1200×, output "width": 1200 in the ImageObject, not 2000. The dimensions should match the contentUrl you're pointing to, not the original upload dimensions.
5 Common ImageObject Mistakes in Shopify
| # | Mistake | Impact | Fix |
|---|---|---|---|
| 1 | Bare string image URL instead of ImageObject | No alt text, no dimensions, no primary signal, no encoding format — AI visual search operates blind on your images | Upgrade to full ImageObject block with contentUrl, description, representativeOfPage, width, height, encodingFormat |
| 2 | Alt text entered in Shopify admin but not mapped to JSON-LD description |
Google structured data crawler and AI indexers don't read HTML alt attributes from JSON-LD context — the admin alt text you wrote is silently invisible to machine indexing |
Explicitly map image.alt to ImageObject.description in the Liquid snippet |
| 3 | Multiple images all with representativeOfPage: true |
Search systems receive conflicting primary-image signals and fall back to algorithmic selection — often choosing a lifestyle shot or detail image over the primary white-background hero shot for Shopping tiles | Set representativeOfPage: true only on the primary product image (product.featured_image). All other images: omit or set false |
| 4 | CDN transformation URL dimensions don't match stated width/height |
Search quality systems flag dimension mismatches as data quality errors. A 2000px wide image served at 1200px via img_url: '1200x' should state width: 1200, not 2000 |
Always output dimensions matching the transformed URL, not the original upload dimensions |
| 5 | Generic filenames and no alt text on non-primary images | Secondary images (back views, detail shots, lifestyle) without alt text contribute zero semantic signal to visual search — AI cannot disambiguate product-back from product-front, or identify styling context | Write descriptive alt text for all images in the Shopify admin. Use format: "[Color] [Product type], [view/angle description]" — e.g., "Navy wool coat, back view showing single vent" |
Frequently Asked Questions
Why does Shopify's default JSON-LD fail for visual search?
Shopify outputs images as bare URL strings — "image": "https://cdn.shopify.com/…/product.jpg" — which discards alt text, dimensions, primary-image designation, and encoding format. AI visual search crawlers and Google Lens need the full ImageObject block to understand what each image depicts without fetching and analyzing every pixel. See Shopify Product Image Alt Text & AI Agents for broader alt text optimization guidance.
What is representativeOfPage and why does it matter?
Set representativeOfPage: true on the primary product image to tell Google's image systems which image to use in Shopping tiles, image carousels, and AI shopping agent product cards. Without this signal, systems guess — and frequently pick a lifestyle or detail shot over the clean hero image. Only one ImageObject per product page should carry representativeOfPage: true.
How does alt text in JSON-LD differ from the HTML alt attribute?
The HTML alt attribute is for screen readers and browser fallback. The ImageObject.description in JSON-LD is what Google's structured data crawler ingests for semantic image indexing. Shopify's default output doesn't bridge these — your carefully-written admin alt text is invisible to structured data crawlers unless you explicitly map it. The Liquid snippet above does this via image.alt.
Should I use 'image' as a string URL or as an ImageObject?
Always prefer the full ImageObject form. Schema.org allows both, but the full object unlocks: representativeOfPage, description (alt text), width/height (aspect ratio), encodingFormat, and thumbnail. For multiple images, "image" accepts an array of ImageObject values. See Shopify Product Images & AI Shopping Agents for broader image optimization strategy.
How do I handle variant-specific images in ImageObject schema?
Use an array of all product images in the Product's image property for the simplest implementation. For variant-specific image linking, add an image property to each Offer object pointing to the variant-specific ImageObject. This tells AI agents which image corresponds to which variant (e.g., a blue variant's Offer links to the blue colorway ImageObject). For full variant schema, see Shopify ProductGroup & Variant Schema.
Are your product images invisible to AI visual search?
CatalogScan audits your Shopify store's structured data and identifies bare-string image URLs, missing alt text in JSON-LD, absent representativeOfPage signals, and ImageObject gaps across your entire catalog.