Fix Missing Alt Text in Shopify Themes: Complete Liquid Template Guide
You added alt text in Shopify admin, but SEO audits still show empty alt attributes. Here's how to fix your theme templates.
You wrote alt text for every product image in your Shopify admin. You checked the image editor, filled in the field, saved. Then you ran an SEO audit and every image shows alt="". Hundreds of accessibility errors.
Shopify stores your alt text correctly—the problem is your theme templates. Dawn (Shopify's default theme) shipped with hardcoded alt="" in collection banners from v7 through v13, and in some product image templates through v15. Shopify has since patched Dawn, but many stores still run older versions—and third-party themes often have the same problem. The fix takes 10 minutes once you know where to look.
Find the Problem
Right-click any image on your storefront and select "Inspect." Look at the img tag:
<!-- This means your theme is ignoring your alt text -->
<img src="product-image.jpg" alt="">
<!-- This is what it should look like -->
<img src="product-image.jpg" alt="Blue cotton t-shirt front view">
Check your homepage (collection cards), collection pages (banners), product pages (featured images and galleries), and blog posts (article images). If DevTools shows alt="" but you entered alt text in the admin, you have a template problem.
The Fix Pattern
Every fix in this guide follows the same pattern. Here's a collection banner as an example. The template lives in sections/main-collection-banner.liquid.
The bug—hardcoded empty alt:
{% if collection.image %}
<img
src="{{ collection.image | image_url: width: 1920 }}"
alt=""
loading="lazy"
>
{% endif %}
The fix—pull the stored alt text with a fallback:
{% if collection.image %}
{%- assign banner_alt = collection.image.alt | default: collection.title -%}
<img
src="{{ collection.image | image_url: width: 1920 }}"
alt="{{ banner_alt | escape }}"
loading="lazy"
>
{% endif %}
Three things happening here:
collection.image.altpulls the alt text you entered in the admin| default: collection.titlefalls back to the collection name if alt text is empty| escapeprevents XSS from quotes in user-entered text
That's it. Every fix below uses this same approach: replace hardcoded alt="" with the object's .alt property, add a default fallback, and escape the output.
Where to Apply It
Collection Cards
File: snippets/card-collection.liquid
{%- assign card_alt = collection.image.alt | default: collection.title -%}
{{ collection.image | image_url: width: 600 | image_tag:
alt: card_alt,
loading: 'lazy',
class: 'collection-card__image'
}}
Product Cards and Featured Images
File: snippets/product-card.liquid or sections/main-product.liquid
{%- assign image_alt = product.featured_image.alt | default: product.title -%}
{{ product.featured_image | image_url: width: 800 | image_tag:
alt: image_alt,
loading: 'lazy',
class: 'product-card__image'
}}
Product Gallery
Product galleries loop through product.media. Each media object has its own alt text ("Front view," "Side view," etc.):
{% for media in product.media %}
{%- if media.media_type == 'image' -%}
{%- assign media_alt = media.alt | default: product.title -%}
{{ media | image_url: width: 1200 | image_tag:
alt: media_alt,
loading: 'lazy',
class: 'product-media__image'
}}
{%- endif -%}
{% endfor %}
Variant Thumbnails
Color swatches and size option thumbnails need alt text too. Use cascading fallbacks—image alt, then variant title ("Blue / Medium"), then product title:
{% for variant in product.variants %}
{% if variant.image %}
{%- assign variant_alt = variant.image.alt
| default: variant.title
| default: product.title -%}
<img
src="{{ variant.image | image_url: width: 100 }}"
alt="{{ variant_alt | escape }}"
class="variant-thumbnail"
>
{% endif %}
{% endfor %}
Blog Article Images
File: sections/main-article.liquid
{% if article.image %}
{%- assign article_image_alt = article.image.alt | default: article.title -%}
{{ article.image | image_url: width: 1200 | image_tag:
alt: article_image_alt,
loading: 'lazy',
class: 'article__featured-image'
}}
{% endif %}
Watch Out: Inline Filters Inside image_tag
Liquid's parser can get confused when you chain filters like | default: directly inside image_tag parameters. The parser may apply the filter to the entire tag output instead of just the alt value. This is especially common with media.preview_image objects.
{% comment %} Risky - Liquid may misparse the | default: filter {% endcomment %}
{{ media.preview_image | image_url: width: 200 | image_tag:
alt: media.alt | default: product.title
}}
{% comment %} Safe - assign handles the fallback first {% endcomment %}
{%- assign safe_alt = media.alt | default: product.title -%}
{{ media.preview_image | image_url: width: 200 | image_tag:
alt: safe_alt
}}
Always use assign for fallback logic before passing to image_tag. It's more readable and avoids parsing surprises. Note that image_tag handles HTML escaping automatically, so you don't need | escape here—only add it when writing raw <img> tags.
Reusable Snippet
Tired of repeating yourself? Create snippets/image-with-alt.liquid:
{%- assign safe_alt = image.alt | default: fallback -%}
<img
src="{{ image | image_url: width: width }}"
alt="{{ safe_alt | escape }}"
loading="lazy"
{% if css_class %}class="{{ css_class }}"{% endif %}
width="{{ image.width }}"
height="{{ image.height }}"
>
Then use it everywhere:
{% render 'image-with-alt',
image: collection.image,
fallback: collection.title,
width: 1920,
css_class: 'collection-banner__image'
%}
{% render 'image-with-alt',
image: product.featured_image,
fallback: product.title,
width: 600,
css_class: 'product-card__image'
%}
One file to maintain instead of fixing the same pattern in dozens of templates.
Test Your Fixes
- Preview first: Use Shopify's theme editor preview mode before going live
- Inspect with DevTools: Check that alt attributes now show your admin text on collection pages, product pages, and blog posts
- Test fallbacks: Create a test product with no alt text to confirm your
defaultlogic works - Re-run your audit: Lighthouse, Screaming Frog, or AltText.ai's analyzer to confirm the errors are gone
Now You Need Actual Alt Text
Fixing templates makes Shopify output whatever you enter in the admin. But if your alt text fields are empty, you've just replaced alt="" with... alt="". The template fix and the content are two separate problems.
For stores with hundreds or thousands of products, writing alt text by hand takes days. The AltText.ai Shopify app generates descriptive alt text automatically when you upload product images and can bulk-process your existing catalog. Once your templates are fixed and your images have real descriptions, your store is accessible to screen readers and performs better in Google image search.
Fix the templates, then let us handle the alt text
The AltText.ai Shopify app generates accurate, SEO-friendly descriptions for every product image. Bulk-process your existing catalog or auto-generate on upload.