> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Image accessibility


Making images accessible to all users, including those using assistive technologies, is essential for creating inclusive digital experiences. This guide covers best practices for implementing text alternatives, managing accessibility metadata at scale with Cloudinary's asset management tools, and leveraging AI-powered solutions to automatically generate descriptive content for your image library.

## Image accessibility considerations
{table:class=no-borders overview accessibility-considerations} Consideration | Cloudinary Image Techniques | WCAG Reference
---|---|---
**Consider how users with visual impairments will understand your images.** They may rely on screen readers that need descriptive text alternatives to convey the same information. | 🔧 [Managing text alternatives](accessible_media_images#managing_text_alternatives)🔧 [AI-based image captioning](accessible_media_images#ai_based_image_captioning) 🔧 [Cloudinary AI Vision](accessible_media_images#cloudinary_ai_vision) | [1.1.1](https://www.w3.org/TR/WCAG22/#non-text-content) Non-text content 

## Text alternatives

Text alternatives are crucial for making visual content accessible to everyone, particularly users with visual impairments, cognitive disabilities, or those using assistive technologies like screen readers. These alternatives provide equivalent information about images, charts, diagrams, and other visual content in a format that can be understood through text-to-speech software, braille displays, or simply read by users who prefer textual descriptions.

![A woman in a stunning white mermaid-style wedding gown stands in an ornate, vintage-inspired room with intricate architectural details and decorative elements.](https://res.cloudinary.com/cld-docs/image/upload/c_scale,w_600/f_auto/q_auto/kcaln5dspbavxxwyuwsd.jpg "popup:true")

```html
<img src="https://res.cloudinary.com/cld-docs/image/upload/c_scale,w_600/f_auto/q_auto/kcaln5dspbavxxwyuwsd.jpg"
 alt="A woman in a stunning white mermaid-style wedding gown stands in an ornate, vintage-inspired room with intricate architectural details and decorative elements." />
```

> **TIP**:
>
> :title=Learn about text alternatives...
> Text alternatives can be used in:

> * **Alt text (alternative text)**: The most common form of text alternative, typically provided through the `alt` attribute in HTML image tags. Alt text should be concise and descriptive, focusing on the essential information the image conveys.

> * **Extended descriptions**: For complex images like charts, diagrams, or infographics that require more detailed explanation than alt text can provide.

> * **Captions and figcaptions**: Visible text that appears alongside images, providing context or description that benefits all users, not just those using assistive technology. 

> * **ARIA labels**: Using `aria-label`, `aria-labelledby`, or `aria-describedby` attributes to provide accessible labels for interactive elements or complex visual content.
> Good alt text should be:

> * **Concise but descriptive**: Aim for one to two sentences that capture the essential information

> * **Context-appropriate**: Consider how the image relates to surrounding content

> * **Functional**: Describe what the image does or represents, not just what it looks like

> * **Equivalent**: Provide the same information the image would convey to a sighted user

> * **Relevant**: Use empty alt text (`alt=""`) for purely decorative images that don't convey meaningful information

Cloudinary provides tools and approaches for managing text alternatives at scale, including:

* **Centralized metadata management**: Store alt text and descriptions with your assets as the single source of truth
* **AI-powered generation**: Automatically generate descriptive alt text using machine learning

## Managing text alternatives

**Cloudinary Assets**, Cloudinary's Digital Asset Management (DAM) product, serves as the single source of truth for managing text alternatives across your entire Media Library. Rather than maintaining alt text in multiple locations throughout your application, you can centralize all accessibility metadata within Cloudinary Assets, enabling consistent management, review, and approval workflows.

### Using contextual metadata for text alternatives

The simplest approach is to use Cloudinary's built-in contextual metadata field, called `alt`, to store text alternatives. You can manage this field through the [Media Library interface](dam_manage_metadata#setting_contextual_metadata_values) or programmatically [via the APIs](contextual_metadata).

![Sample contextual metadata in Asset Management page](https://cloudinary-res.cloudinary.com/image/upload/q_auto/f_auto/bo_1px_solid_grey/v1710093555/docs/DAM/context_metadata_tab.png "thumb: w_700,dpr_2, width:700, with_code:false, with_url:false, popup:true")

Here's an example of setting the `alt` contextual metadata field for an image during upload programmatically:

```multi
|ruby
Cloudinary::Uploader.upload("product-image.jpg", 
  context: { alt: "Red leather handbag with gold hardware and shoulder strap" })

|php_2
$cloudinary->uploadApi()->upload("product-image.jpg", 
  ["context" => ["alt" => "Red leather handbag with gold hardware and shoulder strap"]]);

|python
cloudinary.uploader.upload("product-image.jpg",
  context = {"alt": "Red leather handbag with gold hardware and shoulder strap"})

|nodejs
cloudinary.v2.uploader
.upload("product-image.jpg", 
  { context: { alt: "Red leather handbag with gold hardware and shoulder strap" } })
.then(result=>console.log(result)); 

|java
cloudinary.uploader().upload("product-image.jpg", 
  ObjectUtils.asMap("context", ObjectUtils.asMap("alt", "Red leather handbag with gold hardware and shoulder strap")));

|csharp
var uploadParams = new ImageUploadParams()
{
  File = new FileDescription(@"product-image.jpg"),
  Context = new StringDictionary { { "alt", "Red leather handbag with gold hardware and shoulder strap" } }
};
var uploadResult = cloudinary.Upload(uploadParams); 

|swift
let params = CLDUploadRequestParams()
  .setContext("alt=Red leather handbag with gold hardware and shoulder strap")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "product-image.jpg", params: params) 

|go
resp, err := cld.Upload.Upload(ctx, "product-image.jpg", uploader.UploadParams{
	Context: "alt=Red leather handbag with gold hardware and shoulder strap"})

|cli
cld uploader upload "product-image.jpg" context="alt=Red leather handbag with gold hardware and shoulder strap"
```

Alternatively, you can use any contextual metadata field name to store the text.  

### Using structured metadata for text alternatives

For better standardization across an organization, you can use [structured metadata](structured_metadata) to create custom fields that support validation, approval workflows, and advanced search capabilities.

You can manage structured metadata fields through the [Media Library interface](dam_manage_metadata#setting_structured_metadata_values) or programmatically [via the APIs](structured_metadata).

![Sample structured metadata in Asset Management page](https://cloudinary-res.cloudinary.com/image/upload/q_auto/f_auto/bo_1px_solid_grey/v1710094060/docs/DAM/structured_metadata_tab.png "thumb: w_750,dpr_2, with_code:false, with_url:false, width:750, popup:true")

Here's an example of setting a structured metadata field, with external ID, `asset_description`, on upload:

```multi
|ruby
Cloudinary::Uploader.upload("product-image.jpg", 
  metadata: { asset_description: "Red leather handbag with gold hardware and shoulder strap" })

|php_2
$cloudinary->uploadApi()->upload("product-image.jpg", 
  ["metadata" => ["asset_description" => "Red leather handbag with gold hardware and shoulder strap"]]);

|python
cloudinary.uploader.upload("product-image.jpg",
  metadata = {"asset_description": "Red leather handbag with gold hardware and shoulder strap"})

|nodejs
cloudinary.v2.uploader.upload("product-image.jpg", {
  metadata: {
    asset_description: "Red leather handbag with gold hardware and shoulder strap"
  }
}).then(result => console.log(result));

|java
cloudinary.uploader().upload("product-image.jpg", 
  ObjectUtils.asMap("metadata", ObjectUtils.asMap("asset_description", "Red leather handbag with gold hardware and shoulder strap")));

|csharp
var uploadParams = new ImageUploadParams()
{
  File = new FileDescription(@"product-image.jpg"),
  Metadata = new StringDictionary { { "asset_description", "Red leather handbag with gold hardware and shoulder strap" } }
};
var uploadResult = cloudinary.Upload(uploadParams); 

|swift
let params = CLDUploadRequestParams()
  .setMetadata(["asset_description": "Red leather handbag with gold hardware and shoulder strap"])
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "product-image.jpg", params: params) 

|go
resp, err := cld.Upload.Upload(ctx, "product-image.jpg", uploader.UploadParams{
	Metadata: map[string]interface{}{"asset_description": "Red leather handbag with gold hardware and shoulder strap"}})

|cli
cld uploader upload "product-image.jpg" metadata="asset_description=Red leather handbag with gold hardware and shoulder strap"
```

### Centralized asset management benefits

By storing accessibility metadata directly with your assets in Cloudinary, you gain several key advantages, including:

* Single source of truth:
   * All text alternatives are stored with the asset, ensuring consistency across all implementations
   * No need to maintain separate databases or files for accessibility content
   * Changes to descriptions automatically propagate to all applications using the asset
* Searchable and discoverable:
   * Use the [Search API method](search_method) to find assets by their accessibility descriptions
   * Identify assets missing alt text for remediation
   * Analyze description quality across your Media Library
* Review and approval workflows:
   * Content teams can review and approve accessibility descriptions in the Media Library
   * Implement approval workflows before publishing content
* Bulk operations:
   * Programmatically update multiple assets simultaneously
   * Import accessibility descriptions from external sources
   * Export descriptions for review by accessibility experts

### Integrating with delivery

Once text alternatives are stored as asset metadata, they're automatically available in your delivery implementations:

**JavaScript integration:**

```javascript
// Retrieve asset with metadata
cloudinary.v2.api.resource("product-image", { context: true })
  .then(result => {
    const altText = result.context?.alt || "Product image";
    document.getElementById('product-img').alt = altText;
  });
```

**Product Gallery Widget integration:**

```javascript
// Use alt text from metadata
const gallery = cloudinary.galleryWidget({
  cloudName: "demo",
  mediaAssets: [{ tag: "products" }],
  accessibilityProps: {
    mediaAltSource: "contextual",
    mediaAltId: "alt"
  }
});
```

This centralized approach ensures that accessibility improvements benefit all your applications simultaneously, while providing the tools needed for professional content management and review processes.

## AI-based image captioning

While storing text alternatives as metadata provides centralized management, creating meaningful descriptions for large image libraries can be time-consuming. Using [AI-based image captioning](cloudinary_ai_content_analysis_addon#ai_based_image_captioning), you can programmatically provide captions for images, saving time and resources. 

1. Subscribe to the [Cloudinary AI Content Analysis add-on](cloudinary_ai_content_analysis_addon).
1. Upload an image to your Media Library, invoking AI-based image captioning:
   
    ```multi
    |ruby
    Cloudinary::Uploader.upload("wedding_dress.jpg", 
      detection: "captioning")

    |php_2
    $cloudinary->uploadApi()->upload("wedding_dress.jpg", 
      ["detection" => "captioning"]);

    |python
    cloudinary.uploader.upload("wedding_dress.jpg",
      detection = "captioning")

    |nodejs
    cloudinary.v2.uploader
    .upload("wedding_dress.jpg", 
      { detection: "captioning" })
    .then(result=>console.log(result)); 

    |java
    cloudinary.uploader().upload("wedding_dress.jpg", ObjectUtils.asMap(
      "detection", "captioning"));

    |csharp
    var uploadParams = new ImageUploadParams() 
    {
      File = new FileDescription(@"wedding_dress.jpg"),
      Detection = "captioning"
    };
    var uploadResult = cloudinary.Upload(uploadParams); 

    |go
    resp, err := cld.Upload.Upload(ctx, "wedding_dress.jpg", uploader.UploadParams{
        Detection: "captioning"})

    |android
    MediaManager.get().upload("wedding_dress.jpg")
      .option("detection", "captioning").dispatch();

    |swift
    let params = CLDUploadRequestParams().setDetection("captioning")
    var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
    params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
    let request = cloudinary.createUploader().signedUpload(
      url: "wedding_dress.jpg", params: params) 

    |cli
    cld uploader upload "wedding_dress.jpg" detection="captioning"

    |curl
    curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST -F 'file=@/path/to/wedding_dress.jpg' -F 'detection=captioning' -F 'timestamp=173719931' -F 'api_key=436464676' -F 'signature=a781d61f86a6f818af'
    ```

1. Use the AI-generated caption from the response for the alt text:

    ```json
    {
      "asset_id": "e22081c0b7128ed881b775cc8a39c7fd",
      "public_id": "kcaln5dspbavxxwyuwsd",
      "version": 1748165817,
      "version_id": "8df62fe9ad5621ab19e321233b47918c",
      "signature": "57268696b3a74f8212c01cc12f076bd1aa0503d0",
      "width": 1280,
      "height": 864,
      "format": "jpg",
      "resource_type": "image",
      "created_at": "2025-05-25T09:36:57Z",
      "tags": [],
      "bytes": 338884,
      "type": "upload",
      "etag": "b1461b877c93d898f48488603aa82c23",
      "placeholder": false,
      "url": "http://res.cloudinary.com/cld-docs/image/upload/v1748165817/kcaln5dspbavxxwyuwsd.jpg",
      "secure_url": "https://res.cloudinary.com/cld-docs/image/upload/v1748165817/kcaln5dspbavxxwyuwsd.jpg",
      "asset_folder": "",
      "display_name": "kcaln5dspbavxxwyuwsd",
      "metadata": {
        "my_metadata": [
          "value1",
          "value2"
        ]
      },
      "info": {
        "detection": {
          "captioning": {
            "status": "complete",
            "data": {
              "caption": "A woman in a stunning white mermaid-style wedding gown stands in an ornate, vintage-inspired room with intricate architectural details and decorative elements."
            },
            "schema_version": 1.0,
            "model_version": 4.0
          }
        }
      },
      "original_filename": "wedding-dress",
      "api_key": "614335564976464"
    }

    ```

    Example code:

    ```react
    import React from 'react';

    const ImageWithCaption = ({ imageData }) => {
      // Destructure out the URL and caption from the API response
      const {
        secure_url: src,
        info: {
          detection: {
            captioning: { data: { caption } = {} } = {}
          } = {}
        } = {}
      } = imageData;

      return (
        <img src={src} alt={caption} />
      );
    };

    export default ImageWithCaption;
    ```

    

```html
<img src="https://res.cloudinary.com/cld-docs/image/upload/c_scale,w_600/f_auto/q_auto/kcaln5dspbavxxwyuwsd.jpg"  
alt="A woman in a stunning white mermaid-style wedding gown stands in an ornate, vintage-inspired room with intricate architectural details and decorative elements." />
```

### Alternative ways to invoke AI-based image captioning:

* Define an [upload preset](upload_presets) in the [Cloudinary Console settings](https://console.cloudinary.com/app/settings/upload/presets), which you can use either programmatically, or in your Media Library, when uploading images.
    ![Setting AI captioning in an upload preset](https://res.cloudinary.com/cloudinary/image/upload/bo_1px_solid_gray/f_auto/q_auto/docs/ai-captioning-upload-preset "thumb: c_scale,w_600/dpr_2.0, width: 600, popup: true")
* Use the [Cloudinary Image Captioning](mediaflows_block_reference#cloudinary_image_captioning) block in MediaFlows to generate a caption as part of a workflow, for example, to update the `alt` contextual metadata field on upload. Learn how to build a PowerFlow that [generates multilingual alt text](mediaflows_multilingual_alt_text_powerflow).
    ![Invoking AI captioning using MediaFlows](https://res.cloudinary.com/cloudinary/image/upload/bo_1px_solid_gray/f_auto/q_auto/docs/mediaflows/multilingual_alt_text_flow_asset_upload.png "thumb: c_scale,w_600/dpr_2.0, width: 600, popup: true")
* For images already in your Media Library, use the [update](admin_api#update_details_of_an_existing_resource) method of the Admin API, instead of the [upload](image_upload_api_reference#upload) method of the Upload API.

> **READING**:
>
> * Docs: [AI-based image captioning](cloudinary_ai_content_analysis_addon#ai_based_image_captioning)

> * Blog: [Revolutionizing Image Descriptions With Cloudinary's AI-Powered Captioning Add-on](https://cloudinary.com/blog/ai-powered-captioning-add-on)

> * Blog: [Automating Alt Text Generation for Existing Images in Cloudinary With MediaFlows](https://cloudinary.com/blog/alt-text-generation-existing-images-cloudinary-mediaflows)

> * Video tutorial: [Automatically set alt text for images in a Next.js application](use_ai_to_generate_image_captions_tutorial)

## Cloudinary AI Vision

An alternative to AI-based image captioning is to use the [Cloudinary AI Vision add-on](cloudinary_ai_vision_addon). This has the benefit of analyzing images that are external to Cloudinary, or stored in Cloudinary product environments that you don't own. You just need a valid URL to the image. 

1. Subscribe to the [Cloudinary AI Vision add-on](cloudinary_ai_vision_addon).
1. Send a request to the [Analyze API](analyze_api_reference) asking for a brief description of the image.

    ```curl
    curl 'https://<API_KEY>:<API_SECRET>@api.cloudinary.com/v2/analysis/<CLOUD_NAME>/analyze/ai_vision_general' -d '{
      "source":
      {
        "uri": "https://res.cloudinary.com/cld-docs/image/upload/v1748165817/kcaln5dspbavxxwyuwsd.jpg"
      },
      "prompts": [
        "Describe the image for alt text purposes. Keep it concise."
      ]
    }'
    ```
1. Use the AI-generated response for the alt text:

    ```json
    {
      "limits": {
        "addons_quota": [
          {
            "type": "ai_vision",
            "used_by_request": 2276,
            "remaining": 96798,
            "limit": 100000,
            "reset_time": "2025-03-03T00:00:00Z"
          }
        ]
      },
      "request_id": "1c1a141f712448c7e549f5e4da59cc11",
      "data": {
        "entity": "https://res.cloudinary.com/cld-docs/image/upload/v1748165817/kcaln5dspbavxxwyuwsd.jpg",
        "analysis": {
          "responses": [
            {
              "value": "A mermaid-style wedding dress with a fitted bodice and dramatic tulle skirt photographed in an elegant room with ornate wall moldings and decorative stone urns."
            }
          ],
          "model_version": 1
        }
      }
    }
    ```

    Example code:

    ```react
    import React from 'react';

    const ImageWithCaption = ({ visionData }) => {
      // Destructure the image URL and the AI-generated caption
      const {
        data: {
          entity: src,
          analysis: {
            responses: [{ value: caption } = {}] = []
          } = {}
        } = {}
      } = visionData;

      return (
        <img src={src} alt={caption} />
      );
    };

    export default ImageWithCaption;

    ```

    

```html
<img src="https://res.cloudinary.com/cld-docs/image/upload/c_scale,w_600/f_auto/q_auto/kcaln5dspbavxxwyuwsd.jpg"  
alt="A mermaid-style wedding dress with a fitted bodice and dramatic tulle skirt photographed in an elegant room with ornate wall moldings and decorative stone urns." />
```

> **READING**:
>
> * Docs: [Cloudinary AI Vision add-on](cloudinary_ai_vision_addon)

 