← Insights

Shopify image alt text: what the theme writes when the field is blank, and what to put in it

Leave the field blank and Shopify writes the product title on every photo. Where the field is, which theme code uses it, what to write, and a five-minute check.

12 min read

Contents — 7 sections

Alt text is the only words an image carries, and Shopify writes them for you when you do not

An img element has one place for text, the alt attribute, and the HTML specification's general rule for it is that replacing every image on a page with the text of its alt should not change the meaning of the page. The test the specification suggests is to imagine reading the page to someone over the phone without mentioning that there is an image: whatever you say in its place is the alt text. Google's image guidance calls it the most important attribute for image metadata, and says Google reads it alongside computer vision and the surrounding page to work out what the image is of.

On Shopify the attribute is rarely blank, and that is the part most advice on the subject misses. The image_tag filter, which is how a modern theme renders nearly every image, sets alt by default to the media alt text or, where there is none, to the resource title for article, collection, line item, product and variant images. A product with eight photographs and an empty alt field on each does not ship with eight empty attributes. It ships with the product title, eight times, on eight photographs that show eight different things. The specification's corollary to the general rule is that the alt should not repeat information already provided in the prose next to the image, and the product title is the H1 a few centimetres above the gallery.

That fallback is the whole shape of the problem. Blank alt text on a Shopify product image is not missing; it is a duplicate of the heading, and a reader who cannot see the image, a screen reader, or a crawler deciding whether the third photograph shows the back of the garment or the stitching, all get the same answer from all eight.

The field is on the file, not the product, and it is one field per image

Alt text lives on the media item. In the admin, open Products, open the product, and click any image in the Media card: the alt text field is in the preview that opens, and it saves with the file. Images used by theme sections rather than products live under Content, Files, and each file has the same field. It is the same field under both routes, and it travels with the image everywhere the image is rendered, on the product page, on the collection card, in the cart line, in the theme editor's picker.

The Admin API makes the ownership explicit. The fileUpdate mutation updates alt text on images, videos and 3D models, and the older productUpdateMedia mutation is deprecated in its favour, which is the model in one line: a product image is a file that happens to be attached to a product. The error codes for that mutation set the only hard limit Shopify publishes: an alt value is rejected above 512 characters. Nobody needs 512. The product export CSV carries an Image Alt Text column, which is the practical route for a catalogue of any size.

Variant images are where the model confuses people. A variant image is one of the product's media assigned to a variant; the alt text belongs to the media, not to the variant, so assigning one photograph to three sizes needs one alt, not three. What changes is the fallback. The image_tag reference lists variant images among those that fall back to the resource title, and the variant reference defines that title as each option joined with a slash, so a swatch image rendered from the variant with no alt text of its own can carry alt="Black / M". That is a label, not a description, and on a colour swatch the colour name is usually the one word the alt actually needs, which is why the swatch case is the one place the fallback is nearly right.

The theme decides whether the field is used, and Dawn shows both ways of using it

The admin field is an input. What lands in the HTML is the theme's decision, and Shopify's reference theme contains both patterns within a few files of each other.

The product gallery in Dawn is rendered by product-thumbnail.liquid, which passes media.preview_image through image_url and image_tag with class, loading, sizes and widths and no alt parameter. So the gallery gets the documented default: the alt text where it exists, the product title where it does not. The collection card in card-product.liquid is written by hand as an img element with alt="{{ card_product.featured_media.alt | escape }}", which writes whatever the media object returns for that field and nothing else. Two templates, two behaviours, one admin field. The way to know what a given theme writes is not to read its documentation but to read the rendered HTML, which takes a minute and is the check below.

The third case is the section image. Dawn's image-banner.liquid renders the hero through image_tag with no alt parameter, and the image_tag reference's own example for a file with no alt text renders alt="". A file has no resource title to fall back to, so a banner uploaded to Files with the field blank arrives as an empty attribute, which the browser and every assistive technology treat as decorative.

Any of the three can be overridden in the theme. The filter takes an alt parameter that replaces the default, and a hand-written tag can be given any Liquid expression. The rule on Shopify's own accessibility page for themes is the floor: every img must have an alt attribute, because without one a screen reader announces the file name and path, and decorative images must carry an empty one. The three ways themes fall below that floor are a hero set with background-image in CSS, which has no attribute to carry; a slider or zoom script that clones images into its own markup and drops the attribute on the way; and an app block that renders its own img from its own data with nothing in the field. The first is also a speed problem, and the hero image post covers why an img positioned to fill the section replaces it.

The WAI decision tree settles most cases in three questions. If the image contains text and the same text is present as real text nearby, the alt is empty. If the image is a link or a button and the page would be hard to use without it, the alt is the destination or the action. If the image contributes meaning that is not otherwise on the page, the alt is a brief description that carries that meaning. The specification adds that a purely decorative image that is part of a site-wide design belongs in CSS rather than in the markup, and that a decorative image which does stay in the markup must have the attribute present and empty. WAI's decorative images tutorial explains why empty rather than absent: a text value on a decorative image is audible clutter, and a missing attribute makes some screen readers read out the file name instead.

Run a store through those questions and the answers sort themselves. The homepage hero with the heading rendered as real text over it: empty, which is what Dawn writes by default and which is correct. The payment and delivery icons that sit beside their own captions: empty. The logo in the header, which is a link to the homepage: the store name, because the link has no other text. The trust badge that is an image of the words "Free returns within 30 days" with no matching text nearby: those words. The product gallery: none of the above.

Every photograph in the gallery is there because it shows something the others do not, the front, the back, the label, the stitching, the thing in a hand for scale, the thing on a body, and each alt should say what that photograph shows and nothing more. Written properly, the eight alts on a product differ from each other and none of them is the title. Google's guidance gives the register with a single example: alt="puppy" is better than nothing, alt="Dalmatian puppy playing fetch" is best, and an alt filled with a list of keywords is the case it names as spam. The specification's caption rule applies too. The alt is what stands in for the image; it is not a caption, a title or a place for the material composition, which belongs in the description where a reader can find it.

A working alt for a product photograph names the product as it appears, the view, and the one detail the photograph exists to show: "Linen shirt in oatmeal, back view, showing the box pleat below the yoke". It is a sentence a person could read aloud. It is not the product title, not the SKU, not a sentence about the brand, and it does not begin with "image of", because the element already says that.

Alt text does one job for Google Images and no separate job for answer engines

For search the mechanism is documented and narrow. Google uses the alt text with computer vision and the page's content to understand the subject of the image; that is what places a product photograph in Google Images and Lens against a query, and Google's image guidance also notes that alt text serves as anchor text when the image is a link, which on a collection page every product card is. The image URL in the product's structured data is a separate thing, a required property for the badge and rich result in Google Images, and the product schema post covers what the theme emits there. That is the documented effect, and it is narrow: the right photograph against the right query in the image results, and a page whose images are legible to anything that reads the HTML rather than looks at it.

For generative answers, Google has put its position in writing twice. The AI features page says there are no additional requirements to appear in AI Overviews or AI Mode and no special optimisations necessary, and the guide to generative AI features says that those features can bring in relevant images, and that a site already following the image SEO guidance is already optimising for them. There is no separate alt text for AI. What there is, is the fact that the crawlers behind the other answer engines fetch HTML and read text, and the alt attribute is the only text an image has. A store whose gallery alts are the product title repeated has told those crawlers nothing about its photographs; a store whose alts describe the views has given them a paragraph of extractable product detail that appears nowhere else on the page. The rest of that mechanism, which crawlers are allowed in and what the page needs to say in text, is in how to get your store recommended by ChatGPT.

Check it in five minutes, on the product page and the collection page

Open the best-selling product page in Chrome, open DevTools, and paste this into the Console:

// Every image on the page: which file, what it says, and whether it was written.
const title = document.querySelector('h1')?.textContent.trim();
console.table([...document.images].map((img) => ({
  file: img.currentSrc.split('?')[0].split('/').pop(),
  alt: img.hasAttribute('alt') ? img.alt : '(no attribute)',
  verdict: !img.hasAttribute('alt') ? 'missing'
    : img.alt === '' ? 'decorative'
    : img.alt === title ? 'fallback'
    : 'written',
})));

Read the table by the last column. Every gallery row marked fallback is a photograph carrying the product title, which is the admin field left blank and the theme doing what its documentation says. A gallery row marked decorative is a hand-written tag writing an empty field, and it means the photograph is invisible to a screen reader and to any crawler that reads text. A row marked missing is a theme or app tag with no attribute at all, and it is the one Lighthouse will flag: the accessibility category runs axe-core's image-alt rule, which maps to WCAG success criterion 1.1.1 at level A and rates the impact critical. Lighthouse cannot tell a fallback from a written alt, which is why the console table is the check and Lighthouse is the confirmation.

Then the same snippet on a collection page, where the cards are the hand-written pattern, and once on the homepage, where the rows should be mostly decorative and the exceptions should each have a reason. Finally, read the written rows aloud. If two photographs of the same product have the same alt, one of them was written for the search engine rather than for the photograph, and if any alt begins with the product title followed by a dash and a list, it is the keyword-stuffed case.

Two things the table does not show. A hero set as a CSS background does not appear in document.images at all, so a homepage table with no hero row is a finding. And an image inside a link with no other text, a logo or a swatch, needs its alt to name the destination; the link has no accessible name otherwise.

First, the products that take the revenue. Twenty products at five to eight photographs each is around 150 alts, a sentence apiece, written in the admin with the photograph in front of you. That is the afternoon, and it is the change that matters, because these are the pages Google Images and every answer engine will encounter first.

Second, the theme. Run the console table on each template and fix any missing row: a background-image hero replaced with a positioned img, a hand-written tag given alt="{{ media.alt | escape }}", an app block's settings checked for an alt field or the block replaced. An hour for a developer who knows the theme.

Third, the section images. Go through Content, Files for the images the homepage and collection banners use, and leave the field empty where real text carries the meaning and write it where the image is the message. Thirty minutes.

Fourth, the rest of the catalogue, by export. Fill the Image Alt Text column in the product CSV and re-import, or write to the alt field through fileUpdate, and do it in batches so that a mistake is a batch rather than the catalogue. Time depends on the count, but it is typing, not judgement, once the first twenty have set the pattern.

Then the console table again on the same three pages, and the accessibility audit in Lighthouse once, for the record. It belongs in section five of a store audit, with the robots file and the product node, rather than in a project of its own. If you would rather have it found, ranked against everything else the store is leaving on the table and written up, that is the Store Teardown: €750, five days, and the document is yours whatever you decide afterwards.

Own your growth.Start with the teardown.

Start with a Teardown. Read it, then decide whether you want us to fix what it finds.