{"id":"cloudinary","kind":"sdk","name":"Cloudinary","slug":"cloudinary","description":"Media upload, transformation, and delivery SDKs.","vendor":"Cloudinary","languages":["javascript","typescript","nodejs","python","php","ruby","java","csharp","go","swift","kotlin","dart"],"categories":["media","storage"],"homepage":"https://cloudinary.com","docsUrl":"https://cloudinary.com/documentation","githubUrl":"https://github.com/cloudinary","packages":[{"registry":"npm","name":"cloudinary","url":"https://www.npmjs.com/package/cloudinary"},{"registry":"pypi","name":"cloudinary","url":"https://pypi.org/project/cloudinary/"}],"tags":["images","video"],"skills":[{"name":"cloudinary-docs","url":"https://skills.sh/cloudinary-devs/skills/cloudinary-docs","install":"npx skills add cloudinary-devs/skills","sdk":"cloudinary","key":"cloudinary/cloudinary-docs","description":"Looks up implementation details in the latest Cloudinary docs via the relevant llms.txt file. Use when building code or answering questions relating to image or video uploads, management, SDKs, APIs, webhooks, or integrations. For topics covered by a specialized Cloudinary skill, prefer that skill. Use this skill alongside it when the full use-case requires capabilities outside that skill's scope.","hasContent":true,"content":"---\nname: cloudinary-docs\ndescription: Looks up implementation details in the latest Cloudinary docs via the relevant llms.txt file. Use when building code or answering questions relating to image or video uploads, management, SDKs, APIs, webhooks, or integrations. For topics covered by a specialized Cloudinary skill, prefer that skill. Use this skill alongside it when the full use-case requires capabilities outside that skill's scope.\nlicense: MIT\nmetadata:\n  author: cloudinary\n  version: '1.1.0'\n---\n\n# Cloudinary Documentation\n\nHelps developers integrate Cloudinary into their applications by providing documentation and code examples retrieved directly from the agent-optimized markdown files in the Cloudinary documentation.\n\n## When to Use\n\n- When a user asks questions or requests code implementation relating to image or video upload, management, SDKs, APIs, webhooks, or integrations\n- For topics covered by a more specialized Cloudinary skill (e.g. transformations, React SDK): prefer that skill. Use this skill alongside it when the full use-case also requires capabilities outside that skill's scope.\n- General Cloudinary documentation lookup (account settings, webhooks, DAM features)\n- Looking up specific Cloudinary API endpoints or SDK methods\n- When a specialized Cloudinary skill handles part of a use-case, use this skill to cover the remaining capabilities it doesn't address, not as a substitute for it.\n\n## Sub-file Index Overview\n\nThe main documentation llms.txt file is split into product-specific sub-files. **Go directly to the relevant sub-file** according to the descriptions below. Do not fetch the main llms.txt first unless the topic spans multiple products or you are unsure.\n\n| Product | Topic | Sub-file URL |\n|---|---|---|\n| Image & Video APIs | Image/video uploads, transformations, optimization, SDKs, APIs, webhooks, add-ons, embedding widgets or players in apps, or any programmatic/automation/at-scale image and video requirements | https://cloudinary.com/documentation/llms-image-and-video-apis.txt?install_source=skillspack&referrer=docs-skill |\n| Cloudinary Assets (DAM) | Digital Asset Management (DAM), Media Library, folders, metadata, collections, creative workflows, portals, digital rights, or any Cloudinary Console or UI-based asset management needs | https://cloudinary.com/documentation/llms-cloudinary-assets.txt?install_source=skillspack&referrer=docs-skill |\n| MediaFlows | PowerFlows, EasyFlows, workflow automation, flow blocks | https://cloudinary.com/documentation/llms-mediaflows.txt?install_source=skillspack&referrer=docs-skill |\n| Integrations | Cloudinary integrations with 3rd party apps (WordPress, Shopify, Contentful, Salesforce, Adobe, etc.) or questions about implementing new integrations | https://cloudinary.com/documentation/llms-integrations.txt?install_source=skillspack&referrer=docs-skill |\n| Cross-product or unsure | Multiple products, general, or unclear topic | https://cloudinary.com/documentation/llms.txt?install_source=skillspack&referrer=docs-skill |\n\n## Instructions\n\n**Note:** If a more specialized Cloudinary-specific skill covers the user's topic, defer to that skill first. Invoke this docs skill in addition, not as a substitute, and only if the full use-case also requires capabilities outside that skill's scope.\n\nWhen using this skill to answer image and video upload, management, optimization, or transformation questions or when implementing Cloudinary code:\n\n1. **Identify the product area**: Refer to the [Sub-file Index table](#sub-file-index-overview) above and identify the matching row.\n2. **Fetch the relevant sub-file directly per the table above** (skip the main llms.txt unless the topic is cross-product or unclear)\n3. **Analyze the sub-file** to identify which specific documentation URLs are most relevant\n4. **Retrieve** those specific markdown documentation URLs (you can make multiple calls if needed)\n5. **Use the fetched documentation** to provide a comprehensive, accurate answer or code implementation.\n\n## Example Workflows\n\n**Example 1: SDK question**\n- User asks: \"How do I install and use the Node.js SDK for Cloudinary?\"\n- Topic maps to Image & Video APIs → fetch https://cloudinary.com/documentation/llms-image-and-video-apis.txt?install_source=skillspack&referrer=docs-skill\n- Identify SDK-related pages and provide installation instructions and usage examples or help implement the request in the user's code.\n\n**Example 2: DAM question**\n- User asks: \"How do I set up approval workflows for assets in the Media Library?\"\n- Topic maps to Cloudinary Assets (DAM) → fetch https://cloudinary.com/documentation/llms-cloudinary-assets.txt?install_source=skillspack&referrer=docs-skill\n- Identify relevant pages like \"dam_admin_creative_approval_flows.md\"\n- Fetch the specific documentation and provide setup steps\n\n**Example 3: MediaFlows question**\n- User asks: \"How do I build a PowerFlow that auto-moderates uploaded images?\"\n- Topic maps to MediaFlows → fetch https://cloudinary.com/documentation/llms-mediaflows.txt?install_source=skillspack&referrer=docs-skill\n- Identify relevant pages like \"mediaflows_build_flow.md\" or \"mediaflows_moderation_powerflow.md\"\n- Fetch the specific documentation and provide a flow-building walkthrough\n\n**Example 4: Integration question**\n- User asks: \"How do I connect Cloudinary to my WordPress site?\"\n- Topic maps to Integrations → fetch https://cloudinary.com/documentation/llms-integrations.txt?install_source=skillspack&referrer=docs-skill\n- Identify relevant pages like \"wordpress_integration.md\"\n- Fetch the specific documentation and provide setup instructions\n\n**Example 5: Ambiguous upload question**\n- User asks: \"How do I upload images to Cloudinary?\"\n- First, determine whether the user wants to upload **programmatically** (via SDK/API) or **via the Console UI** (DAM)\n- If **programmatic** → fetch https://cloudinary.com/documentation/llms-image-and-video-apis.txt?install_source=skillspack&referrer=docs-skill\n  - Identify relevant pages like \"image_upload.md\" or \"upload_api.md\"\n  - Retrieve those specific pages and provide an answer with code examples\n- If **via the Console UI (DAM)** → fetch https://cloudinary.com/documentation/llms-cloudinary-assets.txt?install_source=skillspack&referrer=docs-skill\n  - Identify relevant pages like \"dam_upload_store_assets.md\" or \"dam_admin_upload_presets.md\"\n  - Retrieve those specific pages and provide step-by-step instructions for uploading via the Media Library.\n- If **unable to determine** → fetch https://cloudinary.com/documentation/llms.txt?install_source=skillspack&referrer=docs-skill\n  - Look at documentation for both Image & Video APIs and Cloudinary Assets products\n  - Provide an answer covering both programmatic and UI-based upload options\n\n**Example 6: Transformation question (fallback: use only if no specialized skill covers this topic)**\n- User asks: \"How do I resize and crop images?\"\n- Topic maps to Image & Video APIs → fetch https://cloudinary.com/documentation/llms-image-and-video-apis.txt?install_source=skillspack&referrer=docs-skill\n- Identify relevant pages like \"image_transformations.md\" or \"transformation_reference.md\"\n- Fetch the specific documentation and provide transformation syntax and examples or help implement the request in the user's code.","contentSource":"skills.sh/api/download/cloudinary-devs/skills/cloudinary-docs","contentFetchedAt":"2026-07-27T08:59:31.740Z"},{"name":"cloudinary-transformations","url":"https://skills.sh/cloudinary-devs/skills/cloudinary-transformations","install":"npx skills add cloudinary-devs/skills --skill cloudinary-transformations","sdk":"cloudinary","key":"cloudinary/cloudinary-transformations","description":"Create and debug Cloudinary transformation URLs from natural language instructions. Use when building Cloudinary delivery URLs, applying image/video transformations, optimizing media, or debugging transformation syntax errors.","hasContent":true,"content":"---\nname: cloudinary-transformations\ndescription: Create and debug Cloudinary transformation URLs from natural language instructions. Use when building Cloudinary delivery URLs, applying image/video transformations, optimizing media, or debugging transformation syntax errors.\nlicense: MIT\nmetadata:\n  author: cloudinary\n  version: '1.0.4'\n---\n\n# Cloudinary Transformation Rules\n\n## When to Use\n\n- Building Cloudinary delivery/transformation URLs\n- Converting natural language requests to transformation syntax\n- Debugging transformation URLs that aren't working\n- Optimizing images or videos with Cloudinary\n- Applying effects, overlays, resizing, or cropping\n\n## Quick Start\n\n### Default Best Practice: Always Optimize\n\n**Add `f_auto/q_auto` to the end of nearly every transformation URL** (as final components):\n- Automatically delivers optimal format\n- Optimizes quality for best balance of visual quality and file size\n- Reduces bandwidth and improves performance\n\n**Example:** `c_fill,g_auto,w_400,h_300/f_auto/q_auto`\n\n**Exceptions - Don't add optimization when:**\n- Account has \"Optimize By Default\" enabled (already applied automatically)\n- Special quality requirements (use `q_auto:best`, `q_auto:low`, or manual `q_N` instead)\n- Specific format required (replace `f_auto` with `f_png`, `f_jpg`, etc.)\n- Delivering exact original with no modifications\n\n**Examples of common transformations (with optimization):**\n1. Resize: `c_scale,w_400/f_auto/q_auto`\n2. Smart crop: `c_fill,g_auto,h_300,w_400/f_auto/q_auto`\n3. Background removal: `e_background_removal/f_png/q_auto`\n4. Text overlay: `co_yellow,l_text:Arial_40:Hello%20World/fl_layer_apply,g_south/f_auto/q_auto`\n5. Image overlay: `l_logo/c_scale,w_100/fl_layer_apply,g_north_west,x_10,y_10/f_auto/q_auto`\n\n**Important:** All transformation strings shown throughout this skill are illustrative examples to demonstrate syntax and concepts. When generating transformations, choose specific values (dimensions, colors, positions, etc.) based on the user's actual requirements and use case, not the example values shown.\n\n**For debugging:** See [references/debugging.md](references/debugging.md) for detailed troubleshooting steps.\n\n\n## Gathering Requirements\n\nBefore generating a transformation URL, if not already specified, clarify these details based on the user's request:\n\n### For Resize/Crop Requests\n**Required:**\n- At least one dimension (width OR height)\n- Crop behavior if both dimensions specified (fill, pad, scale, limit, etc.)\n\n**Clarify:**\n- Focal point/gravity (especially for cropping): Face detection? Center? Smart auto-detection?\n- Maintain aspect ratio? (if only one dimension, this is automatic)\n\n**Example questions:**\n- \"What dimensions do you need? (width and/or height)\"\n- \"Should this fill the space (may crop) or fit within it (no cropping)?\"\n- \"Any important focal point? (faces, center, specific area)\"\n\n### For AI Transformation Requests\n**Background removal:**\n- Output format needs (PNG for transparency vs JPG with solid background)\n- What to do with transparent area (keep transparent, add color, or gen_fill)\n\n**Generative fill:**\n- Target dimensions or aspect ratio\n- How much extension needed\n\n**Generative replace:**\n- What object to replace (from)\n- What to replace it with (to)\n- Preserve original shape? (for clothing/objects)\n\n**Generative remove:**\n- What object(s) to remove\n- Remove all instances or just one?\n\n**Generative background replace:**\n- Describe desired background (or use auto-generation)\n- Need reproducibility? (consider seed parameter)\n\n### For Video Transformation Requests\n**Trimming:**\n- Start and end time, or duration\n- Seconds or percentage of video\n\n**Codec/format:**\n- Output format needs (MP4, WebM, etc.)\n- Quality requirements (use `vc_auto` if unsure)\n\n**Audio:**\n- Keep or remove audio track\n- If for autoplay, suggest removing audio (`ac_none`)\n\n### Always Recommend\nUnless user specifies otherwise:\n- **Add `f_auto/q_auto` at the end** of transformation URLs (see Quick Start section for exceptions)\n- Use `g_auto` for smart cropping when filling dimensions\n- Consider cost for AI transformations (inform user of transformation credits)\n\n## Quick Reference\n\n### URL Structure\n\n```\nhttps://res.cloudinary.com/<cloud_name>/<asset_type>/<delivery_type>/<transformations>/<version>/<public_id>.<ext>\n```\n\n**Key Rules:**\n- Commas (`,`) separate parameters **within** a component\n- Slashes (`/`) separate components **between** transformations\n- Each component acts on the output of the previous one\n\n### Parameter Types\n\n**Action parameters**: Perform transformations (one action per component: each action transformation should be separated by a slash)\n**Qualifier parameters**: Modify action behavior (in the same component as the action, using commas as separators)\n\nCheck the [Transformation Reference](https://cloudinary.com/documentation/transformation_reference.md?install_source=skillspack&referrer=trans-skill) to determine if a parameter is an action or qualifier.\n\n## Core Transformations\n\n### Resize & Crop\n\n**Dimension value formats:**\n- **Whole numbers** (e.g., `w_400`, `h_300`) = pixels\n- **Decimal values** (e.g., `w_0.5`, `h_1.0`) = percentage of original dimensions (0.5 = 50%, 1.0 = 100%)\n\n**Choosing the right crop mode:**\n\nUse **`c_scale`** when:\n- Resizing while maintaining original aspect ratio\n- Specify only ONE dimension (width OR height)\n- No cropping needed\n- The user intentionally wants to stretch or squash an image by changing the aspect ratio\n\nUse **`c_fill`** when:\n- Must fit exact dimensions (e.g., thumbnail grid, fixed layout)\n- Okay to crop parts of image\n- Combine with `g_auto` for smart cropping, or `g_face` for portraits\n\nUse **`c_fit`** when:\n- Image must fit within dimensions without cropping\n- Okay to have empty space\n- Maintaining full image content is critical\n\nUse **`c_pad`** when:\n- Must fit exact dimensions without cropping\n- Need to fill empty space with background color/blur (videos only)/AI-generated pixels\n- Use with `b_<color>`, `b_auto`, `b_blurred` (blurred background - videos only), or `b_gen_fill`\n\nUse **`c_limit`** when:\n- Set maximum dimensions but don't upscale small images\n- Preserving original quality of small images matters\n\nUse **`c_thumb`** when:\n- Creating thumbnails (typically avatars)\n- Use with `g_face` for face-centered crops\n\nUse **`c_auto`** when:\n- Cloudinary should intelligently crop to interesting content\n- Combine with `g_auto` for best results\n- Good for dynamic content where focal point varies\n\n**Examples:**\n```\nc_scale,w_400                      # Resize width to 400px, maintain aspect ratio\nc_scale,w_0.5                      # Resize to 50% of original width\nc_fill,g_auto,h_300,w_400          # Fill 400x300px dimensions, smart crop\nc_fit,h_300,w_400                  # Fit within dimensions, no crop\nc_pad,b_white,h_300,w_400          # Pad to exact size with white background\nc_pad,w_1.0                        # Pad to original width (100%)\nc_limit,w_1000                     # Limit max width, no upscale\nc_thumb,g_face,h_150,w_150         # Face-centered square thumbnail\nc_auto,g_auto,w_800                # Auto crop to interesting area\n```\n\n**Important**: Always specify a crop mode explicitly. Avoid using both dimensions with `c_scale` (will distort if aspect ratios don't match) - prefer one dimension to maintain aspect ratio.\n\n### Gravity (Focal Point)\n\nGravity determines which part of the image to focus on when cropping:\n\n- **`g_auto`** - Smart detection (recommended for varied content; detects faces, objects, contrast)\n- **`g_face`** - Face detection (portraits, avatars)\n- **`g_center`** - Center position (centered subjects, logos)\n- **`g_north`, `g_south_east`, etc.** - Compass positions (fixed locations, overlay positioning)\n- **`x_N,y_N`** - Custom offsets (integers = pixels, floats = percentage: 0.8 = 80%)\n\n**Examples:**\n```\nc_fill,g_auto,w_400,h_300                      # Smart crop\nc_thumb,g_face,w_200,h_200                     # Face-centered\nl_logo/fl_layer_apply,g_south_east,x_10,y_10  # Logo bottom-right\n```\n\n**Important**: \n- `g_auto` only works with `c_fill`, `c_lfill`, `c_crop`, `c_thumb`, `c_auto`\n- When using x, y, h, w together, use all integers OR all floats (don't mix)\n\n### Format & Quality\n\n**Recommended defaults:**\n- **`f_auto/q_auto`** - Use for most production images (WebP to supported browsers, optimized file size)\n\n**Specific formats** (when requirements dictate):\n- **`f_png`** - Transparency needed (e.g., after background removal)\n- **`f_jpg`** - Force JPEG (remove transparency)\n- **`q_N`** - Manual quality 1-100 (e.g., `q_60` for thumbnails, `q_90` for hero images)\n- **`dpr_auto`** - Retina displays (Chromium-only, requires Client Hints - see limitations below)\n\n**Examples:**\n```\nf_auto/q_auto           # Recommended default\nf_png/q_auto            # PNG with transparency\nq_80                    # Manual 80% quality\n```\n\n**Best Practice**: Use `/` to separate format and quality as distinct components.\n\n#### Responsive Images (`dpr_auto`, `w_auto`)\n\n**`dpr_auto`** - Automatically adapts to device pixel ratio (Retina displays)\n- **Chromium-only** (Chrome, Edge, Opera, Samsung Internet)\n- Requires Client Hints configuration\n- Falls back to `dpr_1.0` on other browsers\n- Does NOT work inside named transformations\n\n**Alternative for universal support:** Use explicit `dpr_2.0` or `<img srcset>` with 1x/2x variants\n\nFor Client Hints configuration, browser compatibility, responsive breakpoints, and framework integration, see [references/responsive-images.md](references/responsive-images.md)\n\n### Effects\n\n**Common effects:**\n- **`e_grayscale`** - Black and white (artistic, accessibility)\n- **`e_sepia`** - Vintage/nostalgic feel\n- **`e_blur:N`** - Blur (privacy, placeholders; N typically 300-2000)\n- **`e_sharpen`** - Enhance clarity (useful after resizing)\n- **`e_cartoonify`** - Illustrated style\n- **`co_rgb:RRGGBB,e_colorize:N`** - Color tint (N = intensity 0-100, for brand theming)\n- **`e_background_removal`** - See AI Transformations section\n\n**Examples:**\n```\ne_blur:800                       # Blur effect\ne_sharpen                        # Enhance clarity\nco_rgb:0044ff,e_colorize:40      # Blue tint at 40%\n```\n\n**Note**: Color (`co_`) is a qualifier - use in same component as `e_colorize`.\n\n### Overlays & Underlays\n\n**Use for:**\n- **`l_<public_id>`** - Image overlays (logos, watermarks, badges)\n- **`u_<public_id>`** - Image underlays (custom backgrounds behind transparent subjects)\n- **`l_text:font_size:text`** - Text overlays (labels, social cards, dynamic text)\n\n**Pattern:**\n1. Declare: `l_<public_id>` or `u_<public_id>` or `l_text:Arial_40:Hello%20World`\n2. Transform (optional): e.g.  `/c_scale,w_100/` or `/o_50/` (opacity)\n3. Apply: `/fl_layer_apply,g_<position>,x_<offset>,y_<offset>`\n\n**Critical: Using `fl_relative` for overlay dimensions:**\n- **Without `fl_relative`**: Dimensions are relative to the **overlay's original size**\n  - Example: `w_1.0` = 100% of the overlay image's width (not useful for small images)\n- **With `fl_relative`**: Dimensions are relative to the **base image's size**\n  - Example: `w_1.0` = 100% of the base image's width (covers entire width)\n  - **Always use `fl_relative`** when sizing overlays as a percentage of the base image\n\n**Examples:**\n```\nl_logo/c_scale,w_100/fl_layer_apply,g_north_west,x_10,y_10                # Logo at 100px\nl_logo/c_scale,fl_relative,w_0.25/fl_layer_apply,g_north_west,x_10,y_10  # Logo at 25% of image width\nl_docs:one_black_pixel/c_scale,fl_relative,h_1.0,w_1.0/o_50/fl_layer_apply # Full-image semi-transparent overlay\nco_yellow,l_text:Arial_40:Hello%20World/fl_layer_apply,g_south            # Text overlay\nu_background/e_background_removal                                          # Custom background\nc_fill,h_400,w_300/l_same_image/c_fill,e_grayscale,h_400,w_300/fl_layer_apply,g_west,x_300 # Side-by-side (600×400)\n```\n\n**Important**: \n- Color (`co_`) is a qualifier — use in the **same component** as text overlay declaration\n- **Always use `fl_relative`** when you want overlay dimensions as a percentage of the base image\n- **Side-by-side / canvas extension**: to place an overlay *beside* the base, offset it past the base edge — the canvas auto-expands. Use `g_west,x_<base_width>` for horizontal or `g_north,y_<base_height>` for vertical.\n\n### Borders & Rounding\n\n- **`r_N`** - Rounded corners (N = radius in pixels; for modern UI, cards)\n- **`r_max`** - Perfect circle (use with square dimensions; avatars, icons)\n- **`bo_NNpx_solid_color`** - Border (frame images, separate from background)\n\n**Examples:**\n```\nr_20                           # 20px rounded corners\nr_max                          # Perfect circle\nbo_5px_solid_black             # 5px black border\nr_20,bo_5px_solid_rgb:0066ff   # Rounded with border (same component)\n```\n\n**Important**: For borders that follow rounded corners, use border as qualifier in same component.\n\n### Background Color\n\n- **`b_color,c_pad`** - Fill empty space with solid color (product images, letterboxing)\n- **`b_auto,c_pad`** - Aautomatically selected background color based on one or more predominant colors in the image\n- **`b_gen_fill,c_pad`** - AI-extended background (change aspect ratio without cropping; see AI Transformations for cost)\n\n**Examples:**\n```\nb_lightblue,c_pad,w_1.0         # Light blue background\nb_auto,c_pad,ar_16:9            # Automatically selected color for background, 16:9\nb_gen_fill,c_pad,ar_1:1         # AI-extended to square\nb_blurred,c_pad,ar_16:9         # Blurred background (videos only), 16:9\n```\n\n**Critical**: Background (`b_`) is a qualifier - use **with** pad crop in same component: `b_color,c_pad,w_X`, NOT `/b_color/`.\n\n### Rotation & Flips\n\n- **`a_90`, `a_180`, `a_270`** - Rotate in 90° increments (correct orientation)\n- **`a_N`** - Rotate by degrees (e.g., `a_-2` to straighten crooked photos)\n- **`a_hflip`** - Horizontal flip (mirror selfies, directional images)\n- **`a_vflip`** - Vertical flip (reflections)\n- **`a_auto_right`/`a_auto_left`** - Auto-rotate based on EXIF orientation\n\n**Examples:**\n```\na_90                    # Rotate 90° clockwise\na_-2                    # Straighten slight tilt\na_hflip                 # Mirror horizontally\na_auto_right            # Auto-fix from EXIF\n```\n\n### Asset Type Matters (Image vs. Video)\n\nMany flags and parameters apply to only one asset type. Applying one to the wrong base often **fails silently** — the URL still returns a valid `200` with no `X-Cld-Error`, just the wrong output. This goes both ways: video-only syntax on an image, and image-only syntax on a video. Always verify the actual output (dimensions, duration, frame count) rather than assuming it worked.\n\n**Common video-only examples** (this is *not* an exhaustive list — ~35 parameters are video-only):\n- **`fl_splice`** (flag) - Concatenate a clip/image onto the video timeline (no image equivalent — to place media side-by-side, offset the overlay to extend the canvas: `fl_layer_apply,g_west,x_<base_width>`)\n- **`du_`, `so_`, `eo_`** - Trim/seek by time (duration, start offset, end offset)\n- **`fps_`** - Set frame rate\n- **`vc_`, `ac_`** - Video / audio codec\n- **`e_boomerang`, `e_progressbar`** - Video-only effects\n\n**When unsure whether a flag or parameter supports your asset type, check the [Transformation Reference](https://cloudinary.com/documentation/transformation_reference.md?install_source=skillspack&referrer=trans-skill) before applying it.**\n\n## Named Transformations\n\nNamed transformations (`t_<name>`) save transformation chains for reuse. Suggest for:\n- Transformations used across multiple assets\n- Complex transformation chains\n- Expensive operations (to enable baseline transformations and reduce costs)\n\n**Baseline transformations** (`bl_<name>`) cache expensive named transformations so they don't need to be regenerated. Use `bl_` instead of `t_` for AI transformations (background removal, generative AI) that will have variations applied. This can reduce costs from 75-230 tx per variation down to 1 tx each after the initial baseline is generated.\n\n**Example:** `bl_bg_removed/c_scale,w_500` - Uses cached background removal result, only pays for resize (1 tx instead of 75 tx)\n\n**Important:** `f_auto`, `dpr_auto`, and `w_auto` don't work inside named transformations - use them directly in URLs: `t_avatar/f_auto/q_auto`\n\nFor complete details, limitations, and baseline transformation examples, see [references/named-transformations.md](references/named-transformations.md)\n\n## Generative AI Transformations\n\n**Proactively suggest these AI transformations when appropriate:**\n\n**Note:** Numbers in parentheses (e.g., 75 tx) indicate additional transformation credits consumed per use. Standard transformations = 1 tx.\n\n- **`e_background_removal`** (75 tx) - Remove backgrounds (e-commerce, profiles; combine with `f_png` or `b_color,c_pad`)\n- **`b_gen_fill`** (50 tx) - Extend backgrounds (change aspect ratio without cropping; use with `c_pad`)\n- **`e_gen_background_replace:prompt_<text>`** (230 tx) - AI-generated backgrounds (custom environments, seasonal variations; high cost)\n- **`e_gen_replace:from_<obj>;to_<new>`** (120 tx) - Swap objects (product variations, colors; use `;preserve_geometry_true` for clothing)\n- **`e_gen_remove:prompt_<text>`** (50 tx) - Remove objects (clean up distractions)\n- **`e_auto_enhance`** (100 tx) - Improve quality (fix poor lighting/exposure)\n- **`e_upscale`** (10-100 tx) - Enlarge without quality loss (low-res to high-res)\n\n**Important:** AI transformations cost significantly more (50-230 tx vs 1 tx). Inform users of costs and consider baseline transformations (e.g., `bl_bg_removed/c_scale,w_500`) to avoid re-processing expensive operations - see [references/named-transformations.md](references/named-transformations.md#baseline-transformations) and [references/transformation-costs.md](references/transformation-costs.md) for details.\n\nFor complete details, syntax, and powerful combinations, see [references/ai-transformations.md](references/ai-transformations.md)\n\n## Video-Specific Transformations\n\n**Critical:** Use `f_auto:video` (not just `f_auto`) to ensure video output - plain `f_auto` may return an image thumbnail.\n\n- **`vc_auto`** - Automatic codec (recommended; optimal for browser/device)\n- **`so_N/eo_M`** - Trim (start/end in seconds; create clips, remove intro/outro)\n- **`ac_none`** - Remove audio (essential for autoplay; reduces file size)\n- **`fps_N`** - Set frame rate (lower = smaller file; standardize rates)\n- **Video resizing** - Same crop modes as images (`c_fill`, `c_scale`, `c_pad`)\n\n**Common patterns:**\n```\nvc_auto/ac_none/f_auto:video/q_auto                      # Autoplay-ready\nso_0/du_10/vc_auto/f_auto:video/q_auto                   # First 10 seconds\nc_scale,w_720/vc_auto/f_auto:video/q_auto                # Resize to 720p width\nc_fill,g_auto,h_720,w_1280/vc_auto/f_auto:video/q_auto  # 720p HD, smart crop\n```\n\nFor complete details including codecs, trimming strategies, and video concatenation, see [references/video-transformations.md](references/video-transformations.md)\n\n## Variables & Conditionals\n\n**Variables** reuse values and create templates:\n```\n$size_300/c_fill,h_$size,w_$size        # Reuse value\n$iw/w_$iw_div_2                         # Half original width (arithmetic)\n```\n\n**Conditionals** adapt transformations dynamically:\n```\nif_w_gt_1000/c_scale,w_1000/if_end                          # Responsive sizing\nif_ar_gt_1.0/c_fill,w_800,h_450/if_else/c_fill,w_450,h_800/if_end  # Orientation handling\n```\n\n**Key rules:**\n- Variable names: alphanumeric, start with letter, no underscores\n- Conditionals: Must close with `if_end`\n- Arithmetic: `add`, `sub`, `mul`, `div` (left-to-right evaluation)\n\nFor complete syntax, arithmetic operations, nested conditionals, and real-world patterns, see [references/advanced-features.md](references/advanced-features.md)\n\n## Self-Validation Checklist\n\n**Before returning a transformation URL, verify:**\n\n1. ✅ **URL structure is complete** (cloud_name, asset_type `/image/` or `/video/` or `/raw/`, delivery_type, public_id)\n2. ✅ **Each component has only one action parameter** (e.g., one crop mode per component)\n3. ✅ **Crop mode is explicit** (don't rely on defaults; avoid both dimensions with `c_scale`)\n4. ✅ **Overlays end with `fl_layer_apply`** in separate component\n5. ✅ **Text strings are URL-encoded** (spaces = `%20`, special chars encoded)\n6. ✅ **Variable names follow rules** (alphanumeric, start with letter, no underscores)\n7. ✅ **`g_auto` compatibility** (only works with `c_fill`, `c_lfill`, `c_crop`, `c_thumb`, `c_auto`)\n8. ✅ **Background as qualifier** (use with pad crop: `b_color,c_pad,w_X`, not `/b_color/`)\n9. ✅ **Format/quality at end** (prefer `f_auto/q_auto` as final components)\n10. ✅ **Flags/parameters match the base asset type** (asset-type-specific syntax — e.g. video-only `fl_splice`, `du_`, `fps_`, `vc_` — often no-ops silently on the wrong base, in either direction; verify the output and check the Asset Type Matters section above)\n11. ✅ **Transformation parameters are valid** (don't make up any parameter names - check against [Transformation Reference](https://cloudinary.com/documentation/transformation_reference.md?install_source=skillspack&referrer=trans-skill))\n\n**Quick syntax check:**\n- Commas separate parameters within a component: `c_fill,g_auto,w_400`\n- Slashes separate components: `c_fill,w_400/f_auto/q_auto`\n- Actions vs qualifiers: Only one action per component, qualifiers modify that action\n\nSee [references/debugging.md](references/debugging.md) for detailed examples of each check.\n\n## Debugging Checklist\n\nWhen a transformation isn't working:\n\n1. **Verify URL structure**: Check that all required URL parts are present:\n   - Cloud name: `/<cloud_name>/`\n   - Asset type: `/image/` or `/video/` or `/raw/`\n   - Delivery type: `/upload/` or `/fetch/` etc.\n   - Public ID at the end\n2. **Check the X-Cld-Error header**: Cloudinary reports errors in the `X-Cld-Error` HTTP response header\n3. **Check parameter names** against [Transformation Reference](https://cloudinary.com/documentation/transformation_reference.md?install_source=skillspack&referrer=trans-skill)\n4. **Check crop mode**: Specify crop mode explicitly; avoid both dimensions with `c_scale` (causes distortion if aspect ratios don't match)\n5. **Verify gravity compatibility**: `g_auto` doesn't work with `c_scale`, `c_fit`, `c_limit`, `c_pad`\n6. **Check action vs qualifier**: Only one action per component, qualifiers in same component\n7. **Verify overlay pattern**: Must end with `fl_layer_apply` component\n8. **Check variable names**: No underscores, must start with letter\n9. **Verify URL encoding**: Text overlays need URL-encoded strings (spaces = `%20`)\n10. **Check auto parameters in named transformations**: `f_auto`, `dpr_auto`, and `w_auto` don't work inside named transformations - use them directly in URLs\n11. **Verify Client Hints for `dpr_auto`/`w_auto`**: These only work on Chromium browsers with Client Hints enabled; fallback to `dpr_1.0` otherwise (see [references/responsive-images.md](references/responsive-images.md) for configuration)\n12. **Video returns image instead of video**: Use `f_auto:video` (not just `f_auto`) for video transformations - plain `f_auto` may return an image thumbnail\n\n### Checking X-Cld-Error Header\n\nThe `X-Cld-Error` header contains error details when a transformation fails. To check it:\n\n**Using browser DevTools:**\n1. Open Developer Tools (Network tab)\n2. Request the transformation URL\n3. Look for `X-Cld-Error` in Response Headers\n\n**Using code (fetch the URL):**\n```javascript\nfetch('https://res.cloudinary.com/demo/image/upload/w_abc/sample.jpg')\n  .then(response => {\n    const error = response.headers.get('x-cld-error');\n    if (error) {\n      console.log('Cloudinary Error:', error);\n    }\n  });\n```\n\n**Common X-Cld-Error messages:**\n- `Invalid width - abc` - Width parameter expects a number\n- `Invalid transformation syntax` - Malformed transformation string\n- `Resource not found` - Asset doesn't exist or public ID is incorrect\n- `Transformation limit exceeded` - Account transformation quota reached\n\n**Online tool:** Use the [X-Cld-Error Inspector](https://cloudinary.com/documentation/advanced_url_delivery_options.md?install_source=skillspack&referrer=trans-skill#x_cld_error_inspector_tool) to check any Cloudinary URL\n\nFor more details, see [Error Handling](https://cloudinary.com/documentation/advanced_url_delivery_options.md?install_source=skillspack&referrer=trans-skill#error_handling)\n\n## Transformation Costs\n\n**Important:** Warn users about high-cost transformations before generating URLs. AI effects cost significantly more than standard transformations (50-230 tx vs 1 tx).\n\nFor complete cost details and cost reduction strategies, see [references/transformation-costs.md](references/transformation-costs.md)\n\n## Additional Resources\n\n### Skill References (Progressive Disclosure)\n- [references/debugging.md](references/debugging.md) - Use when transformations return errors or unexpected results\n- [references/ai-transformations.md](references/ai-transformations.md) - Use when you need AI transformation prompt syntax, cost details, or complex AI combinations\n- [references/video-transformations.md](references/video-transformations.md) - Use when working with video codecs, trimming strategies, concatenation, or creating animated images from videos\n- [references/advanced-features.md](references/advanced-features.md) - Use when building complex logic with variables, conditionals, or arithmetic\n- [references/responsive-images.md](references/responsive-images.md) - Use when implementing responsive images, configuring Client Hints, or using dpr_auto/w_auto\n- [references/transformation-costs.md](references/transformation-costs.md) - Use when optimizing for cost or explaining cost implications to users\n- [references/named-transformations.md](references/named-transformations.md) - Use when creating reusable transformations or reducing costs for repeated operations\n- [references/examples.md](references/examples.md) - Use when you need real-world examples beyond the Quick Start (social cards, e-commerce, responsive images)\n\n### Core Cloudinary Documentation\n- [Transformation Reference](https://cloudinary.com/documentation/transformation_reference.md?install_source=skillspack&referrer=trans-skill) - All parameters\n\n### Image Transformations\n- [Image Transformations Overview](https://cloudinary.com/documentation/image_transformations.md?install_source=skillspack&referrer=trans-skill)\n- [Resizing and Cropping](https://cloudinary.com/documentation/resizing_and_cropping.md?install_source=skillspack&referrer=trans-skill)\n- [Placing Layers on Images](https://cloudinary.com/documentation/layers.md?install_source=skillspack&referrer=trans-skill)\n- [Effects and Enhancements](https://cloudinary.com/documentation/effects_and_artistic_enhancements.md?install_source=skillspack&referrer=trans-skill)\n- [Background Removal](https://cloudinary.com/documentation/background_removal.md?install_source=skillspack&referrer=trans-skill)\n- [Generative AI Transformations](https://cloudinary.com/documentation/generative_ai_transformations.md?install_source=skillspack&referrer=trans-skill)\n- [Face-Detection Based Transformations](https://cloudinary.com/documentation/face_detection_based_transformations.md?install_source=skillspack&referrer=trans-skill)\n- [Custom Focus Areas](https://cloudinary.com/documentation/custom_focus_areas.md?install_source=skillspack&referrer=trans-skill)\n- [Transformation Refiners](https://cloudinary.com/documentation/transformation_refiners.md?install_source=skillspack&referrer=trans-skill)\n- [Animated Images](https://cloudinary.com/documentation/animated_images.md?install_source=skillspack&referrer=trans-skill)\n- [Transformations on 3D Models](https://cloudinary.com/documentation/transformations_on_3d_models.md?install_source=skillspack&referrer=trans-skill)\n- [Conditional Transformations](https://cloudinary.com/documentation/conditional_transformations.md?install_source=skillspack&referrer=trans-skill)\n- [User-Defined Variables and Arithmetic](https://cloudinary.com/documentation/user_defined_variables.md?install_source=skillspack&referrer=trans-skill)\n- [Custom Functions](https://cloudinary.com/documentation/custom_functions.md?install_source=skillspack&referrer=trans-skill)\n\n### Video Transformations\n- [Video Transformations Overview](https://cloudinary.com/documentation/video_manipulation_and_delivery.md?install_source=skillspack&referrer=trans-skill)\n- [Resizing and Cropping](https://cloudinary.com/documentation/video_resizing_and_cropping.md?install_source=skillspack&referrer=trans-skill)\n- [Trimming and Concatenating](https://cloudinary.com/documentation/video_trimming_and_concatenating.md?install_source=skillspack&referrer=trans-skill)\n- [Placing Layers on Videos](https://cloudinary.com/documentation/video_layers.md?install_source=skillspack&referrer=trans-skill)\n- [Effects and Enhancements](https://cloudinary.com/documentation/video_effects_and_enhancements.md?install_source=skillspack&referrer=trans-skill)\n- [Audio Transformations](https://cloudinary.com/documentation/audio_transformations.md?install_source=skillspack&referrer=trans-skill)\n- [Converting Videos to Animated Images](https://cloudinary.com/documentation/videos_to_animated_images.md?install_source=skillspack&referrer=trans-skill)\n- [Conditional Transformations](https://cloudinary.com/documentation/video_conditional_expressions.md?install_source=skillspack&referrer=trans-skill)\n- [User-Defined Variables and Arithmetic](https://cloudinary.com/documentation/video_user_defined_variables.md?install_source=skillspack&referrer=trans-skill)\n\n## Common Mistakes & Best Practices\n\n**Avoid:**\n- ❌ `w_400,h_300` → ✅ `c_scale,w_400` (both dimensions with c_scale distorts image; prefer one dimension)\n- ❌ `c_scale,g_auto,w_400` → ✅ `c_fill,g_auto,w_400` (g_auto doesn't work with c_scale)\n- ❌ `l_logo/fl_layer_apply,g_north_west` → ✅ `l_logo/c_scale,w_100/fl_layer_apply,g_north_west`\n- ❌ `b_lightblue/e_trim` → ✅ `b_lightblue,c_pad,w_1.0/e_trim` (background as qualifier)\n\n**Always:**\n- Prefer `f_auto/q_auto` in separate components over `f_auto,q_auto`\n- Use `g_auto` for smart cropping unless specific focal point needed\n- Specify crop mode with width/height; prefer one dimension with `c_scale`\n- Never guess parameter names - verify against documentation\n","contentSource":"skills.sh/api/download/cloudinary-devs/skills/cloudinary-transformations","contentFetchedAt":"2026-07-27T08:59:31.933Z"},{"name":"cloudinary-react","url":"https://skills.sh/cloudinary-devs/skills/cloudinary-react","install":"npx skills add cloudinary-devs/skills --skill cloudinary-react","sdk":"cloudinary","key":"cloudinary/cloudinary-react","description":"Provides opinionated React SDK patterns for configuration, common integration scenarios, and troubleshooting for frequent errors and TypeScript pitfalls. Use when writing code or answering questions related to the Cloudinary React SDK.","hasContent":true,"content":"---\nname: cloudinary-react\ndescription: Provides opinionated React SDK patterns for configuration, common integration scenarios, and troubleshooting for frequent errors and TypeScript pitfalls. Use when writing code or answering questions related to the Cloudinary React SDK.\nlicense: MIT\nmetadata:\n  author: cloudinary\n  version: '1.0.2'\n---\n\n# Cloudinary React Skill\n\n## When to Use\n\n- When a user is building or debugging Cloudinary in a **React** app (Vite, Create React App, Parcel, etc.).\n- When implementing or fixing: Upload Widget, AdvancedImage/AdvancedVideo, transformations, overlays, image galleries, video player, or signed/unsigned uploads.\n- When the user sees errors like \"createUploadWidget is not a function\", wrong imports from `@cloudinary/url-gen`, upload preset issues, or video player DOM errors.\n\n\n## Quick Start\n\n**Most common operations:**\n1. **Setup**: Create config file with `cld` instance (see Project setup section)\n2. **Display image**: `const img = cld.image('id').resize(fill().width(800)); <AdvancedImage cldImg={img} />`\n3. **Upload Widget**: Script in index.html + poll for `createUploadWidget` in useEffect\n4. **Image overlay**: Use `source(text(...))` or `source(image(...))` - see Import reference table for exact paths\n5. **Signed uploads**: See [references/signed-uploads.md](references/signed-uploads.md)\n6. **Troubleshooting**: See [references/troubleshooting.md](references/troubleshooting.md)\n\n**For TypeScript**: See [references/typescript-patterns.md](references/typescript-patterns.md)\n**For Video Player**: See [references/video-player.md](references/video-player.md)\n\n\n\n## Instructions\n\nWhen helping with Cloudinary in React, follow the patterns and rules below. Use the **exact** import paths and code patterns specified; do not guess subpaths or invent APIs.\n\n---\n\n# Cloudinary React SDK Patterns & Common Errors\n\n**Scope**: These rules apply to **React (web)** with the browser Upload Widget. The **default** is **Vite** (create-cloudinary-react uses Vite). They also work with **other bundlers** (Create React App, Next.js, Parcel, etc.): only **how you read env vars** changes; see **\"Other bundlers (non-Vite)\"** below. Rules-only users: see **\"Project setup (rules-only / without CLI)\"** for the reusable Cloudinary instance, env, Upload Widget (unsigned/signed), and video player. For **React Native** uploads (including signed upload), see: https://cloudinary.com/documentation/react_native_image_and_video_upload?install_source=skillspack&referrer=react-skill#signed_upload — same \"never expose secret, generate signature on backend\" principle, but React Native uses the `upload()` method and backend SDKs differently.\n\n## Official Documentation\n\n**For complete API references and all configuration options**, consult the Cloudinary documentation via **llms.txt**: https://cloudinary.com/documentation/llms.txt?install_source=skillspack&referrer=react-skill\n\nThis skill provides React patterns and common errors. For detailed API options (e.g., all Upload Widget config properties, Video Player API methods), use the documentation markdown files available through llms.txt.\n\n### Key References\n- **Transformation Rules**: https://cloudinary.com/documentation/cloudinary_transformation_rules.md?install_source=skillspack&referrer=react-skill\n- **Transformation Reference**: https://cloudinary.com/documentation/transformation_reference.md?install_source=skillspack&referrer=react-skill\n- **React Image Transformations & Plugins**: https://cloudinary.com/documentation/react_image_transformations.md?install_source=skillspack&referrer=react-skill#plugins\n- **React Video Transformations**: https://cloudinary.com/documentation/react_video_transformations.md?install_source=skillspack&referrer=react-skill\n- **Upload Widget Reference** (complete config options): https://cloudinary.com/documentation/upload_widget_reference.md?install_source=skillspack&referrer=react-skill\n- **Upload Widget (signed uploads)**: https://cloudinary.com/documentation/upload_widget.md?install_source=skillspack&referrer=react-skill#signed_uploads\n- **Cloudinary Video Player** (standalone player): https://cloudinary.com/documentation/cloudinary_video_player.md?install_source=skillspack&referrer=react-skill\n- **Video Player API Reference** (complete API methods): https://cloudinary.com/documentation/video_player_api_reference.md?install_source=skillspack&referrer=react-skill\n- **Video Player React Tutorial**: https://cloudinary.com/documentation/video_player_react_tutorial.md?install_source=skillspack&referrer=react-skill\n- **Upload assets in Next.js (backend signature)**: https://cloudinary.com/documentation/upload_assets_in_nextjs_tutorial.md?install_source=skillspack&referrer=react-skill\n- **Cloudinary Node.js SDK (server-side signing)** — use **v2**: `import { v2 as cloudinary } from 'cloudinary'`; do not use v1 (e.g. 1.47.0). https://cloudinary.com/documentation/node_integration.md?install_source=skillspack&referrer=react-skill\n- **React Native image and video upload (signed)**: https://cloudinary.com/documentation/react_native_image_and_video_upload.md?install_source=skillspack&referrer=react-skill#signed_upload\n- Always consult the official transformation rules when creating transformations\n- Use only officially supported parameters from the transformation reference\n\n**Golden rule for @cloudinary/url-gen:** When generating overlay or transformation code, use **only** the \"Import reference\" table and \"Canonical overlay block\" in these rules. Copy those import paths and usage patterns exactly; do not guess or derive paths (e.g. `text` and `image` are from `qualifiers/source`, not `actions/overlay`; `Position` is from `qualifiers/position`, not `positioning/Position`).\n\n---\n\n# 📋 PATTERNS (How to Do It Right)\n\n## Project setup (rules-only / without CLI)\n\nIf the user is **not** using the create-cloudinary-react CLI and only has these rules, generate the following so they get correct config, env, and widget setup.\n\n**1. Environment (.env)**  \nCreate a `.env` file in the project root with **Vite prefix** (required for client access):\n- `VITE_CLOUDINARY_CLOUD_NAME=my_cloud` (required — use your actual cloud name, **never** the literal string `your_cloud_name` which causes 401)\n- `VITE_CLOUDINARY_UPLOAD_PRESET=my_preset` (optional; required for unsigned upload widget — use your actual preset name)\n- **Restart the dev server** after adding or changing `.env`. Use `import.meta.env.VITE_*` in code, not `process.env`.\n- **If env var still empty in browser after restart**: Vite may cache the old value. Clear `node_modules/.vite/`, restart dev server, and do a hard refresh (Cmd+Shift+R / Ctrl+Shift+F5). If still empty, see \"Vite env not reaching client\" in Common Errors.\n\n**2. Reusable Cloudinary instance (config)**  \nCreate a config file (e.g. `src/cloudinary/config.ts`) so the rest of the app can use a single `cld` instance:\n```ts\nimport { Cloudinary } from '@cloudinary/url-gen';\n\nconst cloudName = import.meta.env.VITE_CLOUDINARY_CLOUD_NAME;\nif (!cloudName) {\n  throw new Error('VITE_CLOUDINARY_CLOUD_NAME is not set. Add it to .env with the VITE_ prefix.');\n}\n\nexport const cld = new Cloudinary({ cloud: { cloudName } });\nexport const uploadPreset = import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET || '';\n```\n- Use **this** pattern for the reusable instance. Everywhere else: `import { cld } from './cloudinary/config'` (or the path the user chose) and call `cld.image(publicId)` / `cld.video(publicId)`.\n\n**3. Upload Widget (unsigned, from scratch)**  \n\n**Strict pattern (always follow this exactly):**\n1. **Script in `index.html`** (required): Add `<script src=\"https://upload-widget.cloudinary.com/global/all.js\" async></script>` to `index.html`. Do **not** rely only on dynamic script injection from React — it's fragile.\n2. **Poll in useEffect** (required): In `useEffect`, poll with `setInterval` (e.g. every 100ms) until `typeof window.cloudinary?.createUploadWidget === 'function'`. Only then create the widget. A single check (even in `onload`) is **not** reliable because `window.cloudinary` can exist before `createUploadWidget` is attached.\n3. **Add a timeout**: Set a timeout (e.g. 10 seconds) to stop polling and show an error if the script never loads. Clear both interval and timeout in cleanup.\n4. **Create widget once**: When `createUploadWidget` is available, create the widget and store it in a **ref**. Clear the interval and timeout. Pass options: `{ cloudName, uploadPreset, sources: ['local', 'camera', 'url'], multiple: false }`.\n5. **Open on click**: Attach a click listener to a button that calls `widgetRef.current?.open()`. Remove the listener in useEffect cleanup.\n\n❌ **Do NOT**: Check only `window.cloudinary` (not enough); do a single check in `onload` (unreliable); skip the script in `index.html`; poll forever without a timeout.\n- **Signed uploads**: Do not use only `uploadPreset`; use the pattern under \"Secure (Signed) Uploads\" (uploadSignature as function, fetch api_key, server includes upload_preset in signature).\n\n**4. Video player**  \n- Use imperative video element only (create with document.createElement, append to container ref, pass to videoPlayer). See \"Cloudinary Video Player (The Player)\" for the full pattern.\n\n**5. Summary for rules-only users**  \n- **Env**: Use your bundler's client env prefix and access (Vite: `VITE_` + `import.meta.env.VITE_*`; see \"Other bundlers\" if not Vite).\n- **Reusable instance**: One config file that creates and exports `cld` (and optionally `uploadPreset`) from `@cloudinary/url-gen`; use it everywhere.\n- **Upload widget**: Script in index.html (required); in useEffect, **poll** until `createUploadWidget` is a function, then create widget once and store in ref; unsigned = cloudName + uploadPreset; signed = use uploadSignature function and backend.\n- **Video player**: Imperative video element (createElement, append to container ref, pass to videoPlayer); dispose + removeChild in cleanup; fall back to AdvancedVideo if init fails.\n\n**If the user is not using Vite:** Use their bundler's client env prefix and access in the config file and everywhere you read env. Examples: Create React App → `REACT_APP_CLOUDINARY_CLOUD_NAME`, `process.env.REACT_APP_CLOUDINARY_CLOUD_NAME`; Next.js (client) → `NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`, `process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`. The rest (cld instance, widget options, video player) is the same.\n\n## Environment Variables\n- **Default: Vite** — Vite requires `VITE_` prefix; use `import.meta.env.VITE_CLOUDINARY_CLOUD_NAME` (not `process.env`). Restart dev server after changing `.env`.\n- ✅ CORRECT (Vite): `VITE_CLOUDINARY_CLOUD_NAME=mycloud` in `.env`; `import.meta.env.VITE_CLOUDINARY_CLOUD_NAME`\n- ❌ **WRONG**: `VITE_CLOUDINARY_CLOUD_NAME=your_cloud_name` — **Never** use the literal placeholder `your_cloud_name`; it causes 401 errors. Use your actual cloud name from the Cloudinary dashboard.\n- ⚠️ **Vite env not reaching client**: If `import.meta.env.VITE_CLOUDINARY_CLOUD_NAME` is still `undefined` in the browser after restarting:\n  1. Clear Vite cache: `rm -rf node_modules/.vite/` (or delete `node_modules/.vite` folder)\n  2. Restart dev server: `npm run dev`\n  3. Hard refresh browser: Cmd+Shift+R (Mac) or Ctrl+Shift+F5 (Windows/Linux)\n  4. If still empty, use a **static config** workaround (create a `cloudinaryConfig.ts` with a hardcoded cloud name for dev, then switch back to env later) and see Common Errors → \"VITE_ prefix required or env var is undefined\"\n\n## Other bundlers (non-Vite)\n- **Only the env access changes.** All other patterns (reusable `cld`, Upload Widget, Video Player, overlays, signed uploads) are bundler-agnostic.\n- **Create React App**: Prefix `REACT_APP_`; access `process.env.REACT_APP_CLOUDINARY_CLOUD_NAME`, `process.env.REACT_APP_CLOUDINARY_UPLOAD_PRESET`. Restart dev server after `.env` changes.\n- **Next.js (client)**: Prefix `NEXT_PUBLIC_` for client; access `process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`, etc. Server-side can use `process.env.CLOUDINARY_*` without `NEXT_PUBLIC_`.\n- **Parcel / other**: Check the bundler's docs for \"exposing environment variables to the client\" (often a prefix or allowlist). Use that prefix and the documented access (e.g. `process.env.*`).\n- **Config file**: In `src/cloudinary/config.ts` (or equivalent), read cloud name and upload preset using the **user's bundler** env API (e.g. for CRA: `process.env.REACT_APP_CLOUDINARY_CLOUD_NAME`). Same `new Cloudinary({ cloud: { cloudName } })` and exports; only the env read line changes.\n\n## Upload Presets\n- **Unsigned** = client-only uploads (no backend). **Signed** = backend required, more secure. See **\"Signed vs unsigned uploads\"** below for when to use which.\n- ✅ Create unsigned upload preset (for simple client uploads): https://console.cloudinary.com/app/settings/upload/presets\n- ✅ Set preset in `.env`: `VITE_CLOUDINARY_UPLOAD_PRESET=your-preset-name`\n- ✅ Use in code: `import { uploadPreset } from './cloudinary/config'`\n- ⚠️ If upload preset is missing, the Upload Widget will show an error message\n- ⚠️ Upload presets must be set to \"Unsigned\" mode for client-side usage (no API key/secret needed)\n- **When unsigned upload fails**: First check that the user configured their upload preset:\n  1. Is `VITE_CLOUDINARY_UPLOAD_PRESET` set in `.env`? (must match preset name exactly)\n  2. Does the preset exist in the dashboard under Settings → Upload → Upload presets?\n  3. Is the preset set to **Unsigned** (not Signed)?\n  4. Was the dev server restarted after adding/updating `.env`?\n\n## Installing Cloudinary packages\n- ✅ **Install the latest**: When adding Cloudinary packages, use `npm install <package>` **with no version** so npm installs the latest compatible version (e.g. `npm install cloudinary-video-player`). In package.json use a **caret range** (e.g. `\"cloudinary-video-player\": \"^1.0.0\"`) so future installs get the latest compatible. Do not pin to an exact version unless you have verified it exists on npm.\n- ✅ **Package names only**: Use **only** these names: `@cloudinary/react`, `@cloudinary/url-gen`, `cloudinary-video-player` (standalone player), `cloudinary` (Node server-side only). Do not invent names (e.g. no `@cloudinary/video-player`).\n- ❌ **WRONG**: `npm install cloudinary-video-player@1.2.3` or `\"cloudinary-video-player\": \"1.2.3\"` (exact pin) — versions may not exist and break installs.\n- ✅ **Correct**: `npm install cloudinary-video-player` (no version) or in package.json: `\"cloudinary-video-player\": \"^1.0.0\"` (caret = latest compatible).\n\n## Import Patterns\n- ✅ Import Cloudinary instance: `import { cld } from './cloudinary/config'`\n- ✅ Import components: `import { AdvancedImage, AdvancedVideo } from '@cloudinary/react'`\n- ✅ Import plugins: `import { responsive, lazyload, placeholder } from '@cloudinary/react'`\n- ✅ **For transformations and overlays**, use **only** the exact paths in \"Import reference: @cloudinary/url-gen\" and the \"Canonical overlay block\" below. Do **not** guess subpaths (e.g. `text` and `image` are from `qualifiers/source`, not `actions/overlay`).\n\n## Import reference: @cloudinary/url-gen (use these exact paths only)\n\n**Rule:** Do not invent or guess import paths for `@cloudinary/url-gen`. Use **only** the paths in the table and canonical block below. Copy the import statements exactly; do not derive paths (e.g. `@cloudinary/url-gen/overlay` exports only `source` — `text` and `image` are from **`qualifiers/source`**; `Position` is from **`qualifiers/position`**, not `positioning/Position`). Wrong paths cause \"module not found\" or \"does not exist\".\n\n| Purpose | Exact import |\n|--------|----------------|\n| Cloudinary instance (config) | `import { Cloudinary } from '@cloudinary/url-gen';` |\n| Resize (fill) | `import { fill } from '@cloudinary/url-gen/actions/resize';` |\n| Resize (scale, for overlays) | `import { scale } from '@cloudinary/url-gen/actions/resize';` |\n| Delivery format/quality | `import { format, quality } from '@cloudinary/url-gen/actions/delivery';` |\n| Format qualifier (auto) | `import { auto } from '@cloudinary/url-gen/qualifiers/format';` |\n| Quality qualifier (auto) | `import { auto as autoQuality } from '@cloudinary/url-gen/qualifiers/quality';` |\n| Effects (e.g. blur) | `import { blur } from '@cloudinary/url-gen/actions/effect';` |\n| Overlay source | `import { source } from '@cloudinary/url-gen/actions/overlay';` |\n| Overlay text / image (source types) | `import { text, image } from '@cloudinary/url-gen/qualifiers/source';` |\n| Overlay image transformation | `import { Transformation } from '@cloudinary/url-gen/transformation/Transformation';` |\n| Position (overlay) | `import { Position } from '@cloudinary/url-gen/qualifiers/position';` |\n| Gravity/compass | `import { compass } from '@cloudinary/url-gen/qualifiers/gravity';` |\n| Text style (overlay) | `import { TextStyle } from '@cloudinary/url-gen/qualifiers/textStyle';` |\n| Types | `import type { CloudinaryImage, CloudinaryVideo } from '@cloudinary/url-gen';` |\n\n**Canonical overlay block (copy these imports and patterns exactly):**\n```ts\n// Overlay imports — text/image from qualifiers/source, NOT actions/overlay\nimport { source } from '@cloudinary/url-gen/actions/overlay';\nimport { text, image } from '@cloudinary/url-gen/qualifiers/source';\nimport { Position } from '@cloudinary/url-gen/qualifiers/position';\nimport { TextStyle } from '@cloudinary/url-gen/qualifiers/textStyle';\nimport { compass } from '@cloudinary/url-gen/qualifiers/gravity';\nimport { Transformation } from '@cloudinary/url-gen/transformation/Transformation';\nimport { scale } from '@cloudinary/url-gen/actions/resize';\n\n// Text overlay (compass with underscores: 'south_east', 'center')\ncld.image('id').overlay(\n  source(text('Hello', new TextStyle('Arial', 60).fontWeight('bold')).textColor('white'))\n    .position(new Position().gravity(compass('center')))\n);\n\n// Image overlay (logo/image with resize)\ncld.image('id').overlay(\n  source(image('logo').transformation(new Transformation().resize(scale().width(100))))\n    .position(new Position().gravity(compass('south_east')).offsetX(20).offsetY(20))\n);\n```\n\n- **Components** (AdvancedImage, AdvancedVideo, plugins) come from **`@cloudinary/react`**, not from `@cloudinary/url-gen`.\n- **Transformation actions and qualifiers** (resize, delivery, effect, overlay, etc.) come from **`@cloudinary/url-gen/actions/*`** and **`@cloudinary/url-gen/qualifiers/*`** with the exact subpaths above.\n- If an import fails, verify the package version (`@cloudinary/url-gen` in package.json) and the [Cloudinary URL-Gen SDK docs](https://cloudinary.com/documentation/sdks/js/url-gen/index.html?install_source=skillspack&referrer=react-skill) or [Transformation Builder reference](https://cloudinary.com/documentation/sdks/js/transformation_builder_reference?install_source=skillspack&referrer=react-skill).\n\n## Creating Image & Video Instances\n- ✅ Create image instance: `const img = cld.image(publicId)`\n- ✅ Create video instance: `const video = cld.video(publicId)` (same pattern as images)\n- ✅ Public ID format: Use forward slashes for folders (e.g., `'folder/subfolder/image'`)\n- ✅ Public IDs are case-sensitive and should not include file extensions\n- ✅ **Sample assets**: Cloudinary may provide sample assets under `samples/`. **Assume they might not exist** (users can delete them); always handle load errors and provide fallbacks (see Image gallery). When they exist, use them for examples and demos instead of requiring uploads first.\n- ✅ **Sample public IDs that may be available** (use for galleries, demos; handle onError if missing):\n  - Images: `samples/cloudinary-icon`, `samples/two-ladies`, `samples/food/spices`, `samples/landscapes/beach-boat`, `samples/bike`, `samples/landscapes/girl-urban-view`, `samples/animals/reindeer`, `samples/food/pot-mussels`\n  - Video: `samples/elephants`\n- ✅ **Default / most reliable**: Start with `samples/cloudinary-icon` for a single image; use the list above for galleries or variety. Prefer uploaded assets when the user has them.\n- ✅ Examples:\n  ```tsx\n  const displayImage = cld.image('samples/cloudinary-icon');\n  const displayVideo = cld.video('samples/elephants');\n  // Gallery: e.g. ['samples/bike', 'samples/landscapes/beach-boat', 'samples/food/spices', ...]\n  ```\n\n## Transformation Patterns\n\n### Image Transformations\n- ✅ Chain transformations on image instance:\n  ```tsx\n  const img = cld.image('id')\n    .resize(fill().width(800).height(600))\n    .effect(blur(800))\n    .delivery(format(auto()))\n    .delivery(quality(autoQuality()));\n  ```\n- ✅ Pass to component: `<AdvancedImage cldImg={img} />`\n\n### Video Transformations\n- ✅ Chain transformations on video instance (same pattern as images):\n  ```tsx\n  const video = cld.video('id')\n    .resize(fill().width(800).height(600))\n    .delivery(format(auto()));\n  ```\n- ✅ Pass to component: `<AdvancedVideo cldVid={video} />`\n- ✅ Video transformations work the same way as image transformations\n\n### Transformation Best Practices\n- ✅ Format and quality must use separate `.delivery()` calls\n- ✅ Always end with auto format/quality: `.delivery(format(auto())).delivery(quality(autoQuality()))` unless user specifies a particular format or quality\n- ✅ Use `gravity(auto())` unless user specifies a focal point\n- ✅ Same transformation syntax works for both images and videos\n\n## Plugin Patterns\n- ✅ **When the user asks for lazy loading or responsive images**: Use the **Cloudinary plugins** from `@cloudinary/react` — `responsive()`, `lazyload()`, `placeholder()` — with `AdvancedImage`. Do not use only native `loading=\"lazy\"` or CSS-only responsive; the Cloudinary plugins handle breakpoints, lazy loading, and placeholders for Cloudinary URLs.\n- ✅ Import plugins from `@cloudinary/react`\n- ✅ Pass plugins as array: `plugins={[responsive(), lazyload(), placeholder()]}`\n- ✅ Recommended plugin order:\n  1. `responsive()` - First (handles breakpoints)\n  2. `placeholder()` - Second (shows placeholder while loading)\n  3. `lazyload()` - Third (delays loading until in viewport)\n  4. `accessibility()` - Last (if needed)\n- ✅ Always add `width` and `height` attributes to prevent layout shift\n- ✅ Example:\n  ```tsx\n  <AdvancedImage\n    cldImg={img}\n    plugins={[responsive(), placeholder({ mode: 'blur' }), lazyload()]}\n    width={800}\n    height={600}\n  />\n  ```\n\n## Responsive Images Pattern\n- ✅ **Responsive images**: Use the Cloudinary `responsive()` plugin with `fill()` resize (not only CSS). **Lazy loading**: Use the Cloudinary `lazyload()` plugin with `AdvancedImage` (not only `loading=\"lazy\"`).\n- ✅ Use `responsive()` plugin with `fill()` resize\n- ✅ Combine with `placeholder()` and `lazyload()` plugins\n- ✅ Example:\n  ```tsx\n  const img = cld.image('id').resize(fill().width(800));\n  <AdvancedImage \n    cldImg={img} \n    plugins={[responsive(), placeholder({ mode: 'blur' }), lazyload()]} \n    width={800}\n    height={600}\n  />\n  ```\n\n## Image gallery with lazy loading and responsive\n- ✅ **When the user asks for an image gallery with lazy loading and responsive**: Use Cloudinary **plugins** with `AdvancedImage`: `responsive()`, `lazyload()`, `placeholder()` (see Plugin Patterns). Use `fill()` resize with the responsive plugin. Add `width` and `height` to prevent layout shift.\n- ✅ **Sample assets in galleries**: Use the sample public IDs from \"Creating Image & Video Instances\" (e.g. `samples/bike`, `samples/landscapes/beach-boat`, `samples/food/spices`, `samples/two-ladies`, `samples/landscapes/girl-urban-view`, `samples/animals/reindeer`, `samples/food/pot-mussels`, `samples/cloudinary-icon`). **Assume any sample might not exist** — users can delete them. Start with one reliable sample (e.g. `samples/cloudinary-icon`) or a short list; add **onError** handling and remove/hide failed images. Prefer **uploaded** assets when available (e.g. from UploadWidget) over samples.\n- ✅ **Handle load errors**: Use `onError` on `AdvancedImage` to hide or remove failed images (e.g. set state to filter out the publicId, or hide the parent). Provide user feedback (e.g. \"Some images could not be loaded. Try uploading your own!\") and upload functionality so users can add their own images.\n- ✅ **Fallback**: Default gallery list can be a subset of the sample list (e.g. `['samples/cloudinary-icon', 'samples/bike', 'samples/landscapes/beach-boat']`); when user uploads, append `result.public_id`. If an image fails to load, remove it from the list or hide it so the UI doesn't show broken images.\n\n## Image Overlays (text or logos)\n- ✅ **When the user asks for image overlays with text or logos**: Use `@cloudinary/url-gen` overlay APIs. Copy imports and patterns from the **\"Import reference\"** table and **\"Canonical overlay block\"** in these rules. Do not import `text` or `image` from `actions/overlay` — they are from **`qualifiers/source`**; only `source` is from `actions/overlay`.\n- ✅ **Import** `source` from `actions/overlay`; **`text` and `image` from `qualifiers/source`**. Also: `Position` from `qualifiers/position`, `TextStyle` from `qualifiers/textStyle`, `compass` from `qualifiers/gravity`, `Transformation` from `transformation/Transformation`, `scale` from `actions/resize`.\n- ✅ **compass()** takes **string** values, with **underscores**: `compass('center')`, `compass('south_east')`, `compass('north_west')`. ❌ WRONG: `compass(southEast)` or `'southEast'` (no camelCase).\n- ✅ **Overlay image**: Use `new Transformation()` **inside** `.transformation()`: `image('logo').transformation(new Transformation().resize(scale().width(100)))`. ❌ WRONG: `image('logo').transformation().resize(...)` (`.transformation()` does not return a chainable object).\n- ✅ **Text overlay**: `fontWeight` goes on **TextStyle**: `new TextStyle('Arial', 60).fontWeight('bold')`. `textColor` goes on the **text source** (chained after `text(...)`): `text('Hello', new TextStyle('Arial', 60)).textColor('white')`.\n- ✅ **Position** is chained **after** `source(...)`, not inside: `source(image('logo').transformation(...)).position(new Position().gravity(compass('south_east')).offsetX(20).offsetY(20))`.\n- ✅ **Image overlay pattern**: `baseImage.overlay(source(image('id').transformation(new Transformation().resize(scale().width(100)))).position(new Position().gravity(compass('south_east')).offsetX(20).offsetY(20)))`. (Import `scale` from `@cloudinary/url-gen/actions/resize` if needed.)\n- ✅ **Text overlay pattern**: `baseImage.overlay(source(text('Your Text', new TextStyle('Arial', 60).fontWeight('bold')).textColor('white')).position(new Position().gravity(compass('center'))))`.\n- ✅ Docs: React Image Transformations and transformation reference for overlay syntax.\n\n## Upload Widget Pattern\n- ✅ Use component: `import { UploadWidget } from './cloudinary/UploadWidget'`\n\n**Strict initialization pattern (always follow this exactly):**\n1. ✅ **Script in `index.html`** (required):\n  ```html\n  <script src=\"https://upload-widget.cloudinary.com/global/all.js\" async></script>\n  ```\n2. ✅ **Poll in useEffect until `createUploadWidget` is available** (required): Use `setInterval` (e.g. every 100ms) to check `typeof window.cloudinary?.createUploadWidget === 'function'`. Only create the widget when this returns `true`. Clear the interval once ready.\n3. ✅ **Add a timeout** (e.g. 10 seconds) to stop polling and show an error state if the script never loads. Clear both interval and timeout in cleanup and when ready.\n4. ✅ **Create widget once**, store in a ref. Cleanup: clear interval, clear timeout, remove click listener.\n\n❌ **Do NOT**: Check only `window.cloudinary` (the function may not be attached yet); do a single check in `onload` (unreliable timing); skip `index.html` and rely only on dynamic injection; poll forever without a timeout.\n\n- ✅ Create unsigned upload preset in dashboard at `settings/upload/presets`\n- ✅ Add to `.env`: `VITE_CLOUDINARY_UPLOAD_PRESET=your_preset_name`\n- ✅ Handle callbacks:\n  ```tsx\n  <UploadWidget\n    onUploadSuccess={(result) => {\n      console.log('Public ID:', result.public_id);\n    }}\n    onUploadError={(error) => {\n      console.error('Upload failed:', error);\n    }}\n  />\n  ```\n- ✅ Upload result contains: `public_id`, `secure_url`, `width`, `height`, etc.\n\n**For complete Upload Widget configuration options** (all properties for `createUploadWidget`), see the official reference: https://cloudinary.com/documentation/upload_widget_reference.md?install_source=skillspack&referrer=react-skill\n\n## Signed vs unsigned uploads (when to use which)\n\n**Unsigned uploads** (simpler, no backend required):\n- Use when: Quick prototypes, low-risk apps, or when anyone with the preset name may upload.\n- Preset: Create an **Unsigned** upload preset in Cloudinary dashboard (Settings → Upload → Upload presets). Put preset name in `.env` as `VITE_CLOUDINARY_UPLOAD_PRESET`.\n- Client: Widget needs only `cloudName` and `uploadPreset`. No API key or secret; no backend.\n- Trade-off: Anyone who knows the preset name can upload. Use only when that is acceptable.\n\n**Signed uploads** (more secure, backend required):\n- Use when: Production apps, authenticated users, or when you need to control who can upload.\n- Preset: Create a **Signed** upload preset in the dashboard. The backend generates a signature using your API secret; the client never sees the secret.\n- Client: Widget gets `api_key` (from your backend), `uploadPreset`, and an `uploadSignature` **function** that calls your backend for each upload. API secret stays on server only.\n- Trade-off: Requires a backend (Node/Express, Next.js API route, etc.) to sign requests. More secure; signature validates each upload.\n\n**Rule of thumb**: **Default to unsigned uploads** unless the user explicitly asks for \"secure\" or \"signed\" uploads. Do not default to signed — it requires a running backend and will fail out of the box. Use **signed** only when the user explicitly requests secure/signed uploads or needs to restrict who can upload.\n\n## Secure (Signed) Uploads\n\n**When to use:** Production apps, authenticated users, or when you need to control who can upload (more secure than unsigned).\n\n**Golden rules:** \n1. Never expose or commit the API secret (server-only)\n2. Use `server/.env` in `.gitignore` for API key/secret\n3. API key can be sent to client; API secret must stay server-only\n\n**Quick implementation:**\n- Use `uploadSignature` as function (not `signatureEndpoint`)\n- Fetch `api_key` from server before creating widget\n- Include `uploadPreset` in widget config\n- Server includes `upload_preset` in signed params\n- Use Cloudinary Node.js SDK v2 on server\n\nFor complete implementation patterns, client/server code examples, and troubleshooting, see [references/signed-uploads.md](references/signed-uploads.md)\n\n## Video Patterns\n\n- ✅ **Display a video** → Use **AdvancedVideo** (`@cloudinary/react`). It just displays a video (with optional transformations). Not a full player.\n- ✅ **A video player** → Use **Cloudinary Video Player** (`cloudinary-video-player`). That is the actual player (styled UI, controls, playlists, etc.).\n\n### ⚠️ IMPORTANT: Two Different Approaches\n\n**1. AdvancedVideo** (`@cloudinary/react`) — For **displaying** a video\n- React component similar to `AdvancedImage`; just displays a video with Cloudinary transformations\n- Not a full \"player\" — it's video display (native HTML5 video with optional controls)\n- Use when: user wants to show/display a video. Works with `cld.video()` like images with `cld.image()`\n\n**2. Cloudinary Video Player** (`cloudinary-video-player`) — The **player**\n- Full-featured video player (styled UI, controls, playlists). Use when the user asks for a \"video player.\"\n- **Use imperative video element only** (create with document.createElement, append to container ref); do not pass a React-managed `<video ref>`. See \"Cloudinary Video Player (The Player)\" below.\n\n### AdvancedVideo (React SDK - For Displaying a Video)\n- ✅ **Purpose**: Display a video with Cloudinary transformations (resize, effects, etc.). It is **not** a full player — it is for showing a video. For a player, use Cloudinary Video Player.\n- ✅ **Package**: `@cloudinary/react` (same as AdvancedImage)\n- ✅ **Import**: `import { AdvancedVideo } from '@cloudinary/react'`\n- ✅ **NO CSS IMPORT NEEDED**: AdvancedVideo uses native HTML5 video - no CSS import required\n- ❌ **WRONG**: `import '@cloudinary/react/dist/cld-video-player.css'` (this path doesn't exist)\n- ✅ **Create video instance**: `const video = cld.video(publicId)` (like `cld.image()`)\n- ✅ **Apply transformations**: Chain transformations like images:\n  ```tsx\n  const video = cld.video('video-id')\n    .resize(fill().width(800).height(600))\n    .delivery(format(auto()));\n  ```\n- ✅ **Use component**:\n  ```tsx\n  <AdvancedVideo\n    cldVid={video}\n    controls\n    autoplay\n    muted\n  />\n  ```\n- ✅ **Documentation**: https://cloudinary.com/documentation/react_video_transformations.md?install_source=skillspack&referrer=react-skill\n\n### Cloudinary Video Player (The Player)\nUse when the user asks for a **video player** (styled UI, controls, playlists). For just **displaying** a video, use AdvancedVideo instead.\n\n**Critical rule: Imperative element only**\n- ❌ Do NOT pass React-managed `<video ref>` (causes removeChild errors)\n- ✅ Use `document.createElement('video')`, append to container ref, pass to `videoPlayer(el, ...)`\n\n**Quick setup:**\n- Package: `npm install cloudinary-video-player`\n- Import: `import { videoPlayer } from 'cloudinary-video-player'` and CSS\n- Source: `player.source({ publicId })` (object, not string)\n- Cleanup: `player.dispose()` then `if (el.parentNode) el.parentNode.removeChild(el)`\n- Always include `posterOptions: { transformation: { startOffset: '0' }, posterColor: '#0f0f0f' }`\n\nFor complete implementation pattern, cleanup, error handling, and troubleshooting, see [references/video-player.md](references/video-player.md)\n\n### When to Use Which?\n- ✅ **Use AdvancedVideo** when: User wants to **display** or **show** a video (no full player). It just displays a video with transformations.\n- ✅ **Use Cloudinary Video Player** when: User asks for a **video player** — the actual player with styled UI, controls, and optional features (playlists, ads, etc.).\n\n## TypeScript Patterns\n\n**Essential TypeScript usage:**\n- Type imports: `import type { CloudinaryImage, CloudinaryVideo } from '@cloudinary/url-gen'`\n- Upload results: Define `CloudinaryUploadResult` interface\n- Environment variables: Create `vite-env.d.ts` with `ImportMetaEnv` interface\n- Avoid `any`: Use proper interfaces or `unknown` with type guards\n- Type refs: `useRef<HTMLVideoElement>(null)`, `useRef<unknown>(null)` for widgets\n\nFor complete TypeScript patterns, type guards, ref typing, and error solutions, see [references/typescript-patterns.md](references/typescript-patterns.md)\n\n## Best Practices\n- ✅ Always use `fill()` resize with automatic gravity for responsive images\n- ✅ Always end transformations with `.delivery(format(auto())).delivery(quality(autoQuality()))` unless the user specifies a format or quality\n- ✅ Use `placeholder()` and `lazyload()` plugins together\n- ✅ Always add `width` and `height` attributes to `AdvancedImage`\n- ✅ Store `public_id` from upload success, not full URL\n- ✅ Video player: use imperative element only; dispose in useLayoutEffect cleanup and remove element with `if (el.parentNode) el.parentNode.removeChild(el)`; always include `posterOptions` with `transformation: { startOffset: '0' }` and `posterColor: '#0f0f0f'` for reliable poster display\n- ✅ Use TypeScript for better autocomplete and error catching\n- ✅ Prefer `unknown` over `any` when types aren't available\n- ✅ Use type guards for runtime type checking\n- ✅ Define interfaces for Cloudinary API responses\n- ✅ Create `vite-env.d.ts` for environment variable typing\n- ✅ Use proper HTML element types for refs\n\n---\n\n# ⚠️ COMMON ERRORS & SOLUTIONS\n\nFor detailed error solutions and troubleshooting, see [references/troubleshooting.md](references/troubleshooting.md)\n\n## Quick Error Reference\n\n### Environment Variable Errors\n- **Env vars**: Wrong prefix, missing VITE_, not restarted → Use correct bundler prefix, restart, clear cache\n- **Imports**: Wrong package/path → Use exact paths from Import reference table\n- **Upload Widget**: \"createUploadWidget is not a function\" → Poll with setInterval, don't check only `window.cloudinary`\n- **Transformations**: Not working → Chain properly, use v2 syntax, separate format/quality\n- **Video Player**: removeChild errors → Use imperative element (createElement), not React ref\n- **TypeScript**: Type errors → See [references/typescript-patterns.md](references/typescript-patterns.md)\n- **Signed uploads**: Invalid signature → See [references/signed-uploads.md](references/signed-uploads.md)\n\n## Most Common Issues\n\n1. **Environment variables undefined**: Clear `node_modules/.vite/`, restart dev server, hard refresh browser\n2. **\"createUploadWidget is not a function\"**: Poll with `setInterval` until `typeof window.cloudinary?.createUploadWidget === 'function'`\n3. **Wrong imports**: Use ONLY paths from Import reference table - don't guess subpaths\n4. **Upload fails**: Check preset exists, is Unsigned (for unsigned), dev server restarted\n5. **Video player errors**: Use imperative element only, include posterOptions\n6. **Overlay issues**: Import `text`/`image` from `qualifiers/source`, not `actions/overlay`\n\nFor complete troubleshooting with detailed solutions, see [references/troubleshooting.md](references/troubleshooting.md)\n","contentSource":"skills.sh/api/download/cloudinary-devs/skills/cloudinary-react","contentFetchedAt":"2026-07-27T08:59:32.523Z"}],"official":true,"generatedAt":"2026-07-27T09:02:29.956Z"}