Open the Media Library on a Strapi project that has been running for a year and sort by alternativeText. Half the field is empty. Most of what remains is a filename like IMG_4032.jpg, the word "image", or a copy of the caption. Nobody planned it that way, and nobody wrote down alt text best practices for the editors either. The field is optional, editors are busy, and the gallery import from the old site never carried descriptions across.
The gap lands on you because the consequences are technical. An accessibility audit flags a WCAG 1.1.1 failure for every image a screen reader announces by filename. Image search guidance explains why search has less to work with. And someone eventually opens a ticket asking for alt text on thousands of legacy assets, which is a content backlog with your name on it.
The writing rules come first: what good alt text does, how short it should be, and why the same photo needs different text depending on where it sits. Then the pipeline: generating alt text with a vision model or Strapi's AI Media Library, rendering it in Next.js 16, and reviewing AI output before it ships.
In Brief
These four points cover the writing and implementation workflow:
- Good alt text gives screen reader users and search crawlers the information an image carries, and Web Content Accessibility Guidelines (WCAG) 2.2 criterion 1.1.1 requires it.
- Write for the image's job on the page. The W3C decision tree says to describe the destination for links, use
alt=""for decoration, and copy the words in images of text. - Automation fixes coverage, but model context limits mean editors still review its output.
- Generate alt text on upload using Strapi's Growth plan AI Media Library, or trigger an external generator via a Strapi media webhook, then render it in Next.js 16.
Together, they keep automation focused on coverage while you retain control over context and quality.
What Alt Text Is and Why It Matters for Your Build
Use the alt attribute on <img> to provide replacement text that a browser or assistive technology can present in place of an image.
<img src="cap.png" alt="Push the cap down and turn it counter-clockwise (from right to left)">Accessibility and WCAG Requirements
WCAG 2.2 Success Criterion 1.1.1 Non-text Content is a Level A criterion: non-text content needs a text alternative that serves the equivalent purpose. The criterion covers these cases:
- The listed exceptions include controls, time-based media, tests, sensory experiences, and CAPTCHAs.
- Pure decoration still requires an implementation that lets assistive technology ignore it.
These requirements make the distinction between missing and deliberately empty alt text important.
When an alt value exists, screen readers read it aloud. WebAIM notes that when the attribute is missing, screen readers don't ignore the image but announce the file name. Only alt="" causes them to skip the image entirely. That is why an empty alternativeText field in your headless CMS produces a worse experience than a deliberately empty string.
Current laws set concrete requirements, and recent extensions changed the deadlines. The US Department of Justice's Americans with Disabilities Act (ADA) Title II web rule requires WCAG 2.1 Level A and AA conformance, which includes 1.1.1, with new compliance dates of April 26, 2027 for public entities serving 50,000 or more people and April 26, 2028 for smaller ones. In the EU, the European Accessibility Act has applied since June 28, 2025 and covers e-commerce services.
Image SEO and Search Visibility
Google's Image SEO documentation says Google combines alt text with computer vision and page content to understand an image, and gives filenames only "very light clues". For linked images, Google uses alt as anchor text. No Google or Bing page publishes a traffic-lift figure for alt text.
Alt Text Best Practices for Writing Descriptions
Good alt text answers one question: what would you lose if this image disappeared?
Describe Purpose, Not Only Appearance
Imagine the image removed. If information goes missing, the image is informative and the alt should supply it. If nothing does, it's decorative.
Purpose can be a feeling rather than an inventory. The W3C's informative images tutorial uses alt="We're family-friendly." for a stock photo, not a list of what's in the frame. Ask what the image is doing on this page before you describe what is in it.
Keep It Concise
Aim for one short sentence with the key point first. The 125-character limit is a screen-reader convention, not a WCAG rule. In a 2010 WebAIM thread, Patrick H. Lauke suggested it seemed related to how JAWS handled alt, and WebAIM provides no defined maximum for alt text.
Practitioners report that major screen readers don't truncate long alt text; the real cost is that listeners can't pause or rewind inside it, which is why shorter text still helps. Published guides commonly recommend keeping alt text around 125 characters or fewer, though one source notes a broader range of 100 to 250 characters. A character cap won't fix bad alt anyway. For anything longer, give the image a long description elsewhere on the page, as the complex images section below describes.
Skip "Image Of" and Filler Phrases
Screen readers already announce that an element is an image, so "image of", "picture of", and "link to" get read twice. Alt is also read together with the preceding text.
| Before | After |
|---|---|
alt="Image of a white house with a boarded front door" | alt="A white house, with a boarded front door." |
alt="Picture of a magnifying lens icon" | alt="Search" |
<img src="images/home-icon.png" alt="Home Page">Home Page | <img src="images/home-icon.png" alt="">Home Page |
The third row comes from Deque's accessibility rules. With "Home Page" in the alt and again in the adjacent text, a screen reader announces "Home Page Home Page". The icon duplicates the label, so it takes an empty alt.
Use Keywords Naturally
Google's Image SEO page warns against keyword stuffing in alt attributes, which hurts the experience and can make your site look like spam. Its spam policies cover keyword stuffing in anchor text too, and alt on a linked image is anchor text.
Google's Search Essentials still recommend words people search for in prominent places, alt text included. If a product name or model number belongs in an accurate description, write it once.
Match Alt Text to Context
The same file needs different alt text in different places. Harvard's accessibility team shows one photo of Hollis Hall in two articles. In a piece titled "Spring is Here!" the alt is Students lounge and work in brightly colored Luxembourg chairs in Harvard Yard. In "Famous residents of Hollis Hall" the same photo gets Hollis Hall, a red brick building, sits directly adjacent to the open grassy space of Harvard Yard.
Apply this to a product photo in your own catalog:
- Hero section: The image sets the scene, and the alt should say what the scene conveys.
- Catalog grid: Where the product name sits in the same link, the W3C's rule for images redundant to nearby text applies, and the alt should be empty.
A vision model sees the pixels in both cases and has no idea which page they landed on. That gap is the reason AI output gets a review step later in this article.
Alt Text Best Practices by Image Type
The W3C's alt decision tree sorts images by five questions: does it contain text, is it in a link or button, does it add meaning, is it purely decorative, and is the use unclear. The four types below cover almost everything in a typical Media Library.
Informative and Functional Images
Informative images get a brief alt that conveys the essential information. Functional image guidance says that anything inside a link or button should describe the action or destination rather than the picture. Examples from the HTML Standard and the W3C:
<!-- Informative -->
<img src="house.jpeg" alt="A white house, with a boarded front door.">
<!-- Functional: linked logo describes the destination -->
<a href="/"><img src="w3c.png" alt="W3C home"></a>
<!-- Functional: icon button describes the action -->
<input type="image" src="searchbutton.png" alt="Search">
<!-- Functional but redundant: link text already carries the meaning -->
<a href="/"><img src="w3c.png" alt=""> W3C Home</a>The W3C explains why "W3C logo" fails the second case: the user needs to know where the link goes, not what the image looks like.
Decorative Images
Decorative images take alt="" with nothing between the quotes. The W3C notes that some screen readers announce the image when a space sits inside them. Leaving the attribute out (WCAG failure F38) or filling it with a placeholder (failure F39) both fail WCAG:
<img src="divider.png" alt=""> <!-- correct -->
<img src="divider.png"> <!-- F38: missing alt -->
<img src="divider.png" alt="spacer"> <!-- F39: placeholder -->Where you can, follow the CSS background guidance and move decorative images to CSS backgrounds instead. The role="presentation" approach also works but has narrower support than an empty alt.
Charts, Diagrams, and Other Complex Images
Complex images need a two-part alternative: a short alt that identifies the image, plus a long description elsewhere on the page. The W3C's tutorial shows four patterns. The figcaption link is the most direct.
<figure role="group">
<img src="chart.png" alt="Bar chart showing monthly and total visitors for the first quarter 2025 for sites 1 to 3">
<figcaption>
<a href="2025-first-qtr.html">Example.com Site visitors Jan to March 2025 text description of the bar chart</a>
</figcaption>
</figure>You can also put the description inline in the figcaption with headings and a data table, or reference it with aria-describedby. The W3C warns that aria-describedby flattens the referenced element into one paragraph, so tables and headings lose their structure. Use the figcaption approach when the data has shape.
Images of Text
The images-of-text guidance says that if the text exists only inside the image, the alt reproduces it word for word. Describe the words, not the font or the drop shadow. If the same text already appears next to the image, the image is redundant and gets alt="".
<img src="access-city.png" alt="Your access to the city.">
<img src="access-city.png" alt=""> Your access to the city.Why Manual Alt Text Breaks Down at Scale
The WebAIM Million 2026 report covered the top one million home pages in February 2026. Its findings show how quickly manual coverage breaks down:
- WebAIM found missing alt text on 53.1% of them.
- Across 66.6 million images, 16.2% had no alt attribute.
- Another 10.8% of images with alt carried questionable or repetitive text such as "image", "graphic", a filename, or a copy of adjacent content.
- One in four linked images lacked alt entirely.
Missing alt is the second most common error WebAIM finds, behind only low-contrast text. These figures show why relying on editor discipline alone leaves gaps.
Contributors skip optional fields: authors leave the field blank, imports don't carry descriptions across, and someone bulk-uploads forty gallery images on a Friday afternoon. Your legacy backlog grows: Hertfordshire's accessibility statement estimated about 85 hours of editor time to fix alt text on decorative images alone. Quality also varies because many contributors don't know the best practices.
Automation closes your coverage gap but not the quality gap. Missing alt is detectable by automated tools, but no tool can judge whether the alt gives an equivalent experience.
How to Automate Alt Text in Your Content Pipeline
You have two routes in Strapi 5: call a vision model from a hook on upload, or turn on the built-in generation in the Media Library. Either way, your frontend needs to render what lands in alternativeText.
Build Your Own With a Vision Model
Strapi's webhooks emit media.create on upload. The media object carries id, documentId, name, alternativeText (null on a fresh upload), width, height, and formats. Your external service can receive that event, call a vision-capable model, and write the result back through the REST upload endpoint with POST /api/upload?id=x and a fileInfo field containing { alternativeText: "..." }.
On Strapi 5.54.0 and later, the MCP server also registers Media Library tools, including media_update_asset, which writes alternativeText. If you already run an AI client against your project, that gives you a third route with no custom webhook receiver.
If you'd rather stay in-process, subscribe to a database lifecycle hook on plugin::upload.file inside bootstrap() in the src/index.ts configuration. Strapi's v5 guidance steers most cases to Document Service middleware. Its lifecycle hooks post names the upload package as an exception. The official docs don't show an upload-file subscription, so treat this snippet as illustrative and untested [TKTK].
// src/index.ts
import type { Core } from '@strapi/strapi';
type UploadFile = {
id: number;
mime?: string;
alternativeText?: string | null;
url: string;
};
type UploadLifecycleEvent = {
result: UploadFile;
};
declare function callVisionModel(imageUrl: string): Promise<string>;
export default {
register() {},
async bootstrap({ strapi }: { strapi: Core.Strapi }) {
strapi.db.lifecycles.subscribe({
models: ['plugin::upload.file'],
async afterCreate(event: UploadLifecycleEvent) {
const file = event.result;
if (!file?.mime?.startsWith('image/') || file.alternativeText) return;
// file.url is relative with the local provider; prefix your Strapi host before sending it out
// Replace callVisionModel with your vision model API wrapper
const alt = await callVisionModel(file.url);
await strapi
.plugin('upload')
.service('upload')
.updateFileInfo(file.id, { alternativeText: alt });
},
});
},
};updateFileInfo exists on the upload service in Strapi's source and takes the file id plus an object with fields such as alternativeText, but the official docs don't cover it, so test the write-back against your Strapi version. The early return on an existing alternativeText keeps the hook from overwriting text an editor already wrote. Vision models can make mistakes, and both OpenAI and Anthropic advise careful review for high-stakes use, so keep the output reviewable.
Automate Alt Text With Strapi's AI Media Library
Strapi's built-in option skips the plumbing. On a Growth plan (Strapi 5.30 and later), the Media Library does three things:
- Generates alt text and a caption for every supported image on upload. The upload dialog reports "Uploaded • Metadata generated".
- Works in bulk. When existing images lack metadata, the settings page offers a Generate metadata button to fill them in the background. Retroactive generation shipped in 5.34 and is still marked Beta.
- Keeps every output editable. A review modal opens after upload with the generated text for each image, and the roadmap commits to editable AI metadata.
These options let you generate metadata for new and existing assets while keeping your editors in control of the final text.
It covers images only: PNG, JPEG, WebP, HEIC, and HEIF. Strapi skips GIF, SVG, and TIFF. The setting is on by default under Settings > Global Settings > Media Library, and you can switch off Strapi AI entirely with ai.enabled: false in config/admin.ts.
Generation draws on Strapi AI credits. Credit allowances depend on your plan (see Strapi pricing), and lighter actions cost fewer credits. The docs don't publish a per-action figure, so check your credit usage in the Settings Overview after a test batch. Strapi AI isn't available on Enterprise plans, so confirm your plan before you plan around it. For screenshots of the full flow, see the AI Media Library walkthrough.
Render Alt Text in Your Next.js 16 Frontend
Strapi 5's REST API returns a flat media object: no data.attributes wrapper, and documentId alongside the numeric id. If you are migrating from Strapi 4, the response format breaking change explains what moved. Strapi doesn't populate media fields by default, so request url, alternativeText, width, and height with REST populate, or select them in the GraphQL API.
The Next.js 16 Image component requires alt, and its docs say to use alt="" for decorative images. A small wrapper handles both cases:
// app/components/StrapiImage.tsx
import Image from 'next/image';
type StrapiMedia = {
documentId: string;
url: string;
alternativeText: string | null;
width: number;
height: number;
};
type Props = { media: StrapiMedia; decorative?: boolean };
export function StrapiImage({ media, decorative = false }: Props) {
const src = media.url.startsWith('http')
? media.url
: `${process.env.NEXT_PUBLIC_STRAPI_URL}${media.url}`;
return (
<Image
src={src}
alt={decorative ? '' : (media.alternativeText ?? '')}
width={media.width}
height={media.height}
/>
);
}The decorative prop lives in your content model, not in the model's output, because page context and author intent determine whether an image is decorative or informative; a vision model cannot reliably infer from pixels alone whether a given image functions as a hero photo or a background texture. The ?? '' fallback keeps the build green when alternativeText is null, which also means it hides missing descriptions. Pair it with a continuous integration (CI) check that queries the Media Library for null alternativeText on informative images. Remote Strapi hosts need an entry in remotePatterns in next.config.ts.
Alt Text Best Practices for Reviewing AI-Generated Output
You need two checks for generated alt text: an automated pass for presence and patterns, and a human pass for meaning.
Audit Coverage and Quality in CI
Use your CI pipeline to cover these automated checks:
- The
alt-textrule fromeslint-plugin-jsx-a11ychecks<img>,<area>,<input type="image">, and<object>. Its<img>requirements requirealt, and an empty string passes. - The
img-redundant-altrule from the same plugin flags alt containing "image", "picture", or "photo". - The
axe-coreimage rule, taggedwcag111and rated critical, checks that an<img>has alt text or a presentation role. - Playwright accessibility testing lets you run
await new AxeBuilder({ page }).analyze()and assert thatviolationsis empty.
These checks catch missing attributes and common patterns before they reach production.
Linters and axe can tell you an image has no alt text, but not whether the alt describes it. In Next.js 16, next lint is gone and next build no longer runs ESLint, so call eslint . in CI and confirm jsx-a11y is in your config. The ESLint configuration docs cover the change.
A Quick Human Review Checklist
Strapi's post-upload modal is where this happens for Media Library uploads, so give your editors something concrete to check against.
- Context fit. Does the text say what the image does on this page? WebAIM's Ellen Ochoa example rejects alt that adds biography the image doesn't show and repeats the body text.
- No hallucinated details. Check that visible image details support every named person, object, number, or label. A wrong description costs you a reader's trust, because users who can't see the image can't catch the error.
- No redundant phrasing. Strip "image of", "graphic of", and anything that duplicates a caption or adjacent link text.
- Correct decorative handling. A model will describe a divider or background texture. The right value there is
alt="", and the right value for a linked logo is the destination.
Run these four checks in the review modal before the text ships. Correct generated output when the model misidentifies the subject, because fixing the grammar on a wrong description still ships a wrong description.
Making Alt Text Part of Your Build
Write alt text for the page the image sits on, keep it short, and reserve alt="" for images that carry no meaning. Then let automation handle the 53.1% problem: generate on upload and in bulk, review in the modal, and lint for presence in CI so your pipeline catches missing alt attributes before production deployment. Turn on AI metadata generation in your Media Library settings, or try the live demo to see a working Strapi project first.




