Solving the Challenge of Writing Alt Text at Scale in Craft CMS Using Automation Tools

Solving the Challenge of Writing Alt Text at Scale in Craft CMS Using Automation Tools

Valerie Gaudette
Valerie Gaudette
August 22, 2025
Last updated : February 15, 2026
August 22, 2025

If you're managing a Craft CMS site with hundreds or thousands of images, you already know the pain: writing unique, descriptive alt text for every single image takes forever. Maybe you've tried to keep up manually, but new images keep coming in faster than you can describe them. Or perhaps you inherited a site where half the images have no alt text at all.

This guide walks you through setting up automated alt text generation in Craft CMS, using AI-powered plugins that can handle thousands of images in minutes. You'll learn how to choose the right tool, configure it properly, generate alt text in bulk, and set up workflows that keep your alt text coverage complete as new images arrive.

Prerequisites

Before getting started with alt text automation, make sure you have:

  • Craft CMS 4.0 or higher (version 5.x recommended for best plugin compatibility)
  • Composer access for installing plugins
  • Admin permissions in your Craft CMS installation
  • API keys for your chosen AI service (OpenAI, Google Cloud Vision, or others)
  • Budget awareness - AI API calls cost money (typically $0.01-0.05 per image)

We've found that sites with more than 500 images see the best return on investment from automation. For smaller sites, the setup time might not be worth it unless you're adding images frequently.

You should also have:

  • Basic understanding of Craft's asset management system
  • Familiarity with Twig templating (for output adjustments)
  • Access to your server's command line for running queue jobs

Step-by-Step Implementation

Step 1: Evaluate Your Current Alt Text Coverage

First, audit what you're working with. Check how many images lack alt text:

-- Run this query in your database to count images without alt text
SELECT COUNT(*) 
FROM assets 
WHERE kind = 'image' 
AND (alt IS NULL OR alt = '');

In Craft's control panel, you can also filter assets to show only those missing alt text by using the search: alt:"".

Step 2: Choose Your Automation Plugin

Based on current options in 2025, here are your best choices:

For simplicity and reliability: AI Alt Text (uses OpenAI)

  • Best for: Teams wanting quick setup with minimal configuration
  • Cost: $19 plugin OpenAI API usage (about $0.02 per image)
  • Installation: composer require heavymetalavo/craft-aialttext

For multilingual sites: AltTextLab

  • Best for: Sites needing alt text in multiple languages (supports 130 languages)
  • Cost: $29 plugin API usage
  • Installation: composer require alttextlab/alt-text-craftcms

For privacy-conscious projects: Altify (uses HuggingFace)

  • Best for: Sites that can't send images to third-party APIs
  • Cost: Free (open source)
  • Installation: composer require fork/craft-altify

For flexibility: Altomatic

  • Best for: Teams wanting to switch between AI providers
  • Supports: OpenAI, Google Cloud Vision, AWS Rekognition, Azure
  • Installation: composer require codegrain/altomatic

Step 3: Install and Configure Your Chosen Plugin

Let's walk through setting up AI Alt Text as an example:

# Install via Composer
composer require heavymetalavo/craft-aialttext

# Install the plugin in Craft
./craft plugin/install ai-alt-text

Now configure in the control panel:

1. Navigate to Settings → Plugins → AI Alt Text
2. Add your OpenAI API key
3. Select your model (gpt-4o-mini works well and costs less)
4. Set your prompt template:

Describe this image for someone who cannot see it. 
Focus on: main subject, important details, context.
Keep it under 125 characters.
Avoid: redundant phrases like "image of" or "picture showing".

Step 4: Set Up Asset Field Layouts

Make alt text a required field for all image volumes:

1. Go to Settings → Assets → [Your Volume]
2. Click "Field Layout"
3. Drag the "Alternative Text" field into the layout
4. Click the gear icon and check "This field is required"
5. Save the layout

This ensures all new uploads will need alt text (either manual or generated).

Step 5: Generate Alt Text for Existing Images

For bulk generation, use the command line for better performance:

# Queue all images missing alt text
./craft ai-alt-text/generate/missing

# Process the queue (add --verbose to see progress)
./craft queue/run --verbose

For selective generation, use the control panel:

1. Go to Assets
2. Filter to show images without alt text: alt:""
3. Select the images you want to process
4. Choose "Generate Alt Text" from the element actions menu

Step 6: Set Up Automatic Generation for New Uploads

In your plugin settings, enable "Auto-generate on upload". This creates alt text immediately when images are added.

For more control, create an event listener:

// modules/sitemodule/SiteModule.php
use craft\events\AssetEvent;
use craft\services\Assets;
use yii\base\Event;

Event::on(
    Assets::class,
    Assets::EVENT_AFTER_SAVE_ASSET,
    function(AssetEvent $event) {
        $asset = $event->asset;
        
        // Only process new images without alt text
        if ($event->isNew && $asset->kind === 'image' && empty($asset->alt)) {
            // Queue alt text generation
            \Craft::$app->queue->push(new GenerateAltTextJob([
                'assetId' => $asset->id
            ]));
        }
    }
);

Step 7: Update Your Templates

Ensure your templates use the alt field correctly:

{# Basic implementation #}
{% set image = entry.featuredImage.one() %}
{% if image %}
    
{% endif %} {# With fallback handling #} {% set image = entry.featuredImage.one() %} {% if image %} {% set altText = image.alt ?: image.title ?: 'Image related to ' ~ entry.title %}
{% endif %}

Code Examples with Explanations

Custom Prompt Based on Asset Context

Sometimes you want different prompts for different types of images:

// Custom module to vary prompts by asset folder
use craft\elements\Asset;

public function getCustomPrompt(Asset $asset): string
{
    $folder = $asset->getFolder();
    
    switch ($folder->name) {
        case 'products':
            return 'Describe this product image focusing on: item type, color, distinguishing features. Max 100 characters.';
        
        case 'team':
            return 'Describe this person professionally without mentioning physical appearance. Focus on: setting, professional context. Max 100 characters.';
        
        case 'blog':
            return 'Describe the main subject and context of this image. Include relevant details for article readers. Max 125 characters.';
        
        default:
            return 'Describe this image concisely for someone who cannot see it. Max 125 characters.';
    }
}

Batch Processing with Progress Tracking

For large libraries, track progress and handle failures:

// Console command for batch processing with recovery
public function actionProcessBatch($limit = 100)
{
    $assets = Asset::find()
        ->kind('image')
        ->alt(':empty:')
        ->limit($limit)
        ->all();
    
    $processed = 0;
    $failed = [];
    
    foreach ($assets as $asset) {
        try {
            // Generate alt text
            $altText = $this->generateAltText($asset);
            
            // Save to asset
            $asset->alt = $altText;
            Craft::$app->elements->saveElement($asset);
            
            $processed  ;
            $this->stdout("Processed: {$asset->filename}\n");
            
            // Rate limiting
            usleep(500000); // 0.5 second delay
            
        } catch (\Exception $e) {
            $failed[] = $asset->id;
            $this->stdout("Failed: {$asset->filename} - {$e->getMessage()}\n");
        }
    }
    
    $this->stdout("\nCompleted: {$processed} processed, " . count($failed) . " failed\n");
    
    if (!empty($failed)) {
        file_put_contents('failed-assets.txt', implode("\n", $failed));
        $this->stdout("Failed asset IDs saved to failed-assets.txt\n");
    }
}

Multi-language Alt Text Generation

For sites with multiple languages:

{# Template handling for multi-language alt text #}
{% set image = entry.heroImage.one() %}
{% if image %}
    {% switch craft.app.language %}
        {% case 'es' %}
            {% set altField = 'altEs' %}
        {% case 'fr' %}
            {% set altField = 'altFr' %}
        {% default %}
            {% set altField = 'alt' %}
    {% endswitch %}
    
    
{% endif %}

Common Mistakes to Avoid

1. Not Setting API Rate Limits

Many developers forget to configure rate limiting and burn through their API credits quickly. Our experience shows that processing more than 100 images per minute often triggers API throttling. Add delays between requests:

// Add this to your generation loop
usleep(500000); // 0.5 second delay between API calls

2. Ignoring Context-Specific Images

Product images need different descriptions than blog images. Don't use the same generic prompt for everything. Create specific prompts for different asset folders or entry types.

3. Forgetting to Handle API Failures

APIs fail. Networks timeout. Always implement retry logic:

$maxRetries = 3;
$attempt = 0;

while ($attempt < $maxRetries) {
    try {
        $result = $this->callApi($image);
        break; // Success, exit loop
    } catch (\Exception $e) {
        $attempt  ;
        if ($attempt >= $maxRetries) {
            throw $e; // Re-throw after max attempts
        }
        sleep(2 ** $attempt); // Exponential backoff
    }
}

4. Not Reviewing Generated Text

AI sometimes generates inaccurate or inappropriate descriptions. Always review alt text for:

  • Accuracy (does it describe what's actually in the image?)
  • Appropriateness (no unintended bias or inappropriate language)
  • Context (does it make sense for where the image is used?)

5. Overwriting Existing Alt Text

Be careful not to replace manually written alt text with AI-generated content. Check before overwriting:

if (empty($asset->alt) || $asset->alt === $asset->title) {
    // Safe to generate new alt text
    $asset->alt = $this->generateAltText($asset);
}

Testing and Verification Steps

1. Verify Alt Text Coverage

Run this SQL query to check your progress:

SELECT 
    COUNT(*) as total_images,
    COUNT(CASE WHEN alt IS NOT NULL AND alt != '' THEN 1 END) as with_alt,
    COUNT(CASE WHEN alt IS NULL OR alt = '' THEN 1 END) as without_alt,
    ROUND(COUNT(CASE WHEN alt IS NOT NULL AND alt != '' THEN 1 END) * 100.0 / COUNT(*), 2) as coverage_percent
FROM assets
WHERE kind = 'image';

2. Test with Screen Readers

Download NVDA (Windows) or use VoiceOver (Mac) to test your alt text:

1. Navigate to a page with images
2. Turn on the screen reader
3. Tab through the images
4. Listen to how the alt text sounds
5. Check if it provides useful information

3. Validate HTML Output

Check your rendered HTML for proper alt attributes:

// Browser console test
const images = document.querySelectorAll('img');
const missingAlt = Array.from(images).filter(img => !img.hasAttribute('alt') || img.alt === '');
console.log(`Found ${missingAlt.length} images without alt text`);
missingAlt.forEach(img => console.log(img.src));

4. Monitor API Usage

Set up monitoring for your API costs:

// Log API usage to track costs
Craft::info(
    sprintf('Generated alt text for asset %d, API cost: $%.4f', $asset->id, $apiCost),
    'alt-text-generation'
);

5. Quality Spot Checks

Randomly review 10-20 generated descriptions each week:

1. Pick random images from different categories
2. Compare the alt text to the actual image
3. Note any patterns in errors or improvements needed
4. Adjust your prompts based on findings

Conclusion

Setting up automated alt text generation in Craft CMS saves hours of manual work while improving your site's accessibility and SEO. The key is choosing the right plugin for your needs, configuring it thoughtfully, and maintaining quality through regular reviews.

Remember that automation is a tool to help you scale, not a complete replacement for human judgment. The best results come from combining AI generation with manual review for critical images and regular quality checks.

Working with teams has taught us that the most successful implementations start small, perhaps with a single asset folder or entry type, then expand once you're comfortable with the quality and workflow.

Ready to implement automated alt text generation but need help choosing the right approach for your specific Craft CMS setup? We can review your image library, recommend the most cost-effective automation method, and help you establish quality control processes that ensure your alt text actually helps your users. Get in touch to discuss your accessibility automation needs.

Share this article