> ## Documentation Index
> This page is part of the Image and Video APIs product. Fetch the complete documentation index for Image and Video APIs at: https://cloudinary.com/documentation/llms-image-and-video-apis.txt?referrer=docpage and then use it to discover all relevant pages before exploring further.
> If your task extends beyond this product, fetch the top-level index covering all Cloudinary products and topics at: https://cloudinary.com/documentation/llms.txt?referrer=docpage

# Roll out an image transformation


[named-trans-link]: named_transformations#creating_named_transformations
[named-trans-limits-link]: named_transformations#limitations_of_named_transformations
[named-trans-page-link]: named_transformations
[optimization-link]: image_optimization#optimization_best_practices
Once you've designed and validated an image transformation that you want to use across many images, there's a bit more to do before it's ready for production. The goal is to have a single definition of that transformation that your whole app references, so that every image using it is delivered in its optimized form and already generated by the time your users request it.

Following the procedure on this page gives you three things:

* **One place to change the transformation.** A [named transformation][named-trans-page-link] replaces a long list of parameters with a short alias, so when the design changes you update one definition instead of hunting through your codebase.
* **Optimized delivery.** Keeping `f_auto` and `q_auto` out of the named transformation and chaining them in the delivery URL lets the CDN pick the best format and quality per request.
* **A warm cache.** [Eager transformations](eager_and_incoming_transformations#eager_transformations) generate the derived images up front, so your first visitor doesn't wait for them to be created on the fly.

> **TIP**: If you haven't settled on the transformation itself yet, experiment first in [Studio](https://console.cloudinary.com/app/image/studio), and come back once you're happy with the result.

## Step 1: Create a named transformation
Start by saving your transformation under a name, **leaving out the `f_auto` and `q_auto` optimization parameters**. You'll add those back in the delivery URL in [step 2](#step_2_deliver_the_named_transformation_with_optimization).

Why leave the optimization out? `f_auto` isn't applied at all when it's buried inside a named transformation: the CDN chooses the format per request based on the requesting browser, and it can't see parameters hidden behind the alias. `q_auto` does work inside a named transformation, but it loses the ability to downgrade to `q_auto:eco` when a browser asks for reduced data. Keeping both out of the named transformation also makes the optimization visible to anyone reading the URL. For more details, see [Limitations of named transformations][named-trans-limits-link].

So if the transformation you validated was:

```
ar_1:1,c_fill,g_auto,w_600/f_auto/q_auto
```

the named transformation, here called `product_card`, should contain only:

```
ar_1:1,c_fill,g_auto,w_600
```

There are three ways to create it, and for most people one of the first two is the easiest.

### Create a named transformation with an AI agent
The [Environment Config MCP server](cloudinary_llm_mcp#available_mcp_servers) manages product environment entities including named transformations and upload presets, so an AI agent connected to it can create the transformation for you, for example:

```
Create a named transformation called "product_card" that crops images to a 1:1
square 600 pixels wide, automatically focusing on the most interesting area.
```

For installation instructions, see [MCP servers](cloudinary_llm_mcp#mcp_servers).

### Create a named transformation in the Console

Build the transformation step-by-step in [Studio](https://console.cloudinary.com/app/image/studio) and save it with your chosen name. You can also start from an existing example in the [Playground](https://console.cloudinary.com/app/image/playground), click **Use it**, refine it, and save.
Your saved named transformations are listed in the Console under **[Image > Templates > Named Transformations](https://console.cloudinary.com/app/image/manage/named?page=0)**, for both image and video named transformations.

For more details, see [Creating named transformations][named-trans-link].

### Create a named transformation with the Admin API
Call the [create a named transformation](admin_api#create_a_named_transformation) method of the Admin API:

```multi
|nodejs
cloudinary.v2.api.create_transformation(
  "product_card",
  "ar_1:1,c_fill,g_auto,w_600")
.then(result => console.log(result));

|python
cloudinary.api.create_transformation(
  "product_card",
  "ar_1:1,c_fill,g_auto,w_600")

|ruby
Cloudinary::Api.create_transformation(
  "product_card",
  "ar_1:1,c_fill,g_auto,w_600")

|php_2
$api->createTransformation(
  "product_card",
  "ar_1:1,c_fill,g_auto,w_600");

|java
api.createTransformation("product_card",
  "ar_1:1,c_fill,g_auto,w_600", ObjectUtils.emptyMap());

|csharp
var result = cloudinary.CreateTransform(
  new CreateTransformParams(){
    Name = "product_card",
    Transformation = new Transformation().AspectRatio("1:1").Crop("fill").Gravity("auto").Width(600)});

|go
resp, err := cld.Admin.CreateTransformation(ctx, admin.CreateTransformationParams{
  Name: "product_card",
  Transformation: "ar_1:1,c_fill,g_auto,w_600"})

|curl
curl \
  -d 'transformation=ar_1:1,c_fill,g_auto,w_600' \
  -X POST \
  https://<API_KEY>:<API_SECRET>@api.cloudinary.com/v1_1/<CLOUD_NAME>/transformations/product_card

|cli
cld admin create_transformation product_card "ar_1:1,c_fill,g_auto,w_600"
```
> **NOTE**: If you're updating an existing named transformation rather than creating a new one, be aware that existing derived images that use it {valeExclude}are **not**{/valeExclude} automatically regenerated. See [Regenerating derived assets after updating a named transformation](admin_api#regenerating_derived_assets_after_updating_a_named_transformation).

## Step 2: Deliver the named transformation with optimization
With the named transformation saved, deliver it by chaining the optimization parameters after it:

```
https://res.cloudinary.com/<your_cloud_name>/image/upload/t_product_card/f_auto/q_auto/product-photo
```

Here's that pattern working on a real asset. The `demo` product environment has a named transformation called `fill_square_400`, defined as `ar_1:1,c_fill,g_auto,w_400`, delivered here with the optimization chained after it. Switch between the tabs to see the delivery URL and the equivalent SDK code:

![Named transformation delivered with chained optimization](https://res.cloudinary.com/demo/image/upload/t_fill_square_400/f_auto/q_auto/lighthouse_reflection "thumb: w_400,dpr_2, width:400, caption: t_fill_square_400 with f_auto and q_auto chained after it")

```nodejs
cloudinary.image("lighthouse_reflection", {transformation: [
  {transformation: ["fill_square_400"]},
  {quality: "auto"}
  ]})
```

```react
import { name } from "@cloudinary/url-gen/actions/namedTransformation";
import { format, quality } from "@cloudinary/url-gen/actions/delivery";
import { auto as autoFormat } from "@cloudinary/url-gen/qualifiers/format";
import { auto as autoQuality } from "@cloudinary/url-gen/qualifiers/quality";

new CloudinaryImage("lighthouse_reflection")
  .namedTransformation(name("fill_square_400"))
  .delivery(format(autoFormat()))
  .delivery(quality(autoQuality()));
```

```vue
import { name } from "@cloudinary/url-gen/actions/namedTransformation";
import { format, quality } from "@cloudinary/url-gen/actions/delivery";
import { auto as autoFormat } from "@cloudinary/url-gen/qualifiers/format";
import { auto as autoQuality } from "@cloudinary/url-gen/qualifiers/quality";

new CloudinaryImage("lighthouse_reflection")
  .namedTransformation(name("fill_square_400"))
  .delivery(format(autoFormat()))
  .delivery(quality(autoQuality()));
```

```angular
import { name } from "@cloudinary/url-gen/actions/namedTransformation";
import { format, quality } from "@cloudinary/url-gen/actions/delivery";
import { auto as autoFormat } from "@cloudinary/url-gen/qualifiers/format";
import { auto as autoQuality } from "@cloudinary/url-gen/qualifiers/quality";

new CloudinaryImage("lighthouse_reflection")
  .namedTransformation(name("fill_square_400"))
  .delivery(format(autoFormat()))
  .delivery(quality(autoQuality()));
```

```js
import { name } from "@cloudinary/url-gen/actions/namedTransformation";
import { format, quality } from "@cloudinary/url-gen/actions/delivery";
import { auto as autoFormat } from "@cloudinary/url-gen/qualifiers/format";
import { auto as autoQuality } from "@cloudinary/url-gen/qualifiers/quality";

new CloudinaryImage("lighthouse_reflection")
  .namedTransformation(name("fill_square_400"))
  .delivery(format(autoFormat()))
  .delivery(quality(autoQuality()));
```

```python
CloudinaryImage("lighthouse_reflection").image(transformation=[
  {'transformation': ["fill_square_400"]},
  {'quality': "auto"}
  ])
```

```php
use Cloudinary\Transformation\NamedTransformation;
use Cloudinary\Transformation\Delivery;
use Cloudinary\Transformation\Format;
use Cloudinary\Transformation\Quality;

(new ImageTag('lighthouse_reflection'))
	->namedTransformation(NamedTransformation::name("fill_square_400"))
	->delivery(Delivery::format(
	Format::auto()))
	->delivery(Delivery::quality(
	Quality::auto()));
```

```java
cloudinary.url().transformation(new Transformation()
  .named("fill_square_400").chain()
  .quality("auto")).imageTag("lighthouse_reflection");
```

```ruby
cl_image_tag("lighthouse_reflection", transformation: [
  {transformation: ["fill_square_400"]},
  {quality: "auto"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Named("fill_square_400").Chain()
  .Quality("auto")).BuildImageTag("lighthouse_reflection")
```

```dart
cloudinary.image('lighthouse_reflection').transformation(Transformation()
	.namedTransformation(NamedTransformation.name("fill_square_400"))
	.delivery(Delivery.format(
	Format.auto()))
	.delivery(Delivery.quality(
	Quality.auto())));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setNamed("fill_square_400").chain()
  .setQuality("auto")).generate("lighthouse_reflection")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .named("fill_square_400").chain()
  .quality("auto")).generate("lighthouse_reflection");
```

```flutter
cloudinary.image('lighthouse_reflection').transformation(Transformation()
	.namedTransformation(NamedTransformation.name("fill_square_400"))
	.delivery(Delivery.format(
	Format.auto()))
	.delivery(Delivery.quality(
	Quality.auto())));
```

```kotlin
cloudinary.image {
	publicId("lighthouse_reflection")
	 namedTransformation(NamedTransformation.name("fill_square_400"))
	 delivery(Delivery.format(
	Format.auto()))
	 delivery(Delivery.quality(
	Quality.auto())) 
}.generate()
```

```jquery
$.cloudinary.image("lighthouse_reflection", {transformation: [
  {transformation: ["fill_square_400"]},
  {quality: "auto"}
  ]})
```

```react_native
import { name } from "@cloudinary/url-gen/actions/namedTransformation";
import { format, quality } from "@cloudinary/url-gen/actions/delivery";
import { auto as autoFormat } from "@cloudinary/url-gen/qualifiers/format";
import { auto as autoQuality } from "@cloudinary/url-gen/qualifiers/quality";

new CloudinaryImage("lighthouse_reflection")
  .namedTransformation(name("fill_square_400"))
  .delivery(format(autoFormat()))
  .delivery(quality(autoQuality()));
```
The `t_` prefix applies the named transformation, and the `f_auto` and `q_auto` components that follow stay visible to the CDN so it can pick the best format and quality for each request. For more on what these two parameters do, see [Optimize images][optimization-link].

Note that the delivery URL above has no file extension. The extension counts as part of the transformation, so leaving it off means `f_auto` alone determines the delivered format, and you only need one eager transformation per format in [step 3](#step_3_warm_the_cache_with_eager_transformations) rather than one per format and extension combination.

If your product environment has the **Automatic format** [optimize by default setting](optimize_by_default_settings) enabled, that setting is **ignored** on any URL containing a named transformation. Adding `t_` to your URLs therefore turns off the automatic format you were relying on, unless you chain `f_auto` explicitly as shown above. For more details, see [Default automatic format](optimize_by_default_settings#default_automatic_format).

If you already have images in production that use the same transformation as directly defined parameters, in delivery URLs or in SDK calls, replace those with the named transformation now. A global find and replace across your codebase is usually enough. From then on, changing how the transformation looks means updating the named transformation's definition in one place, with no code change or deployment. Bear in mind that images already derived with the old definition aren't regenerated automatically, so you also need to [invalidate and regenerate](admin_api#regenerating_derived_assets_after_updating_a_named_transformation) them.

> **NOTE**: Swapping directly defined parameters for a named transformation changes the delivery URL, so the result is a new derived image the first time it's requested. The previously derived images stay in storage until you remove them. See [Transformation counts](transformation_counts) for how this affects your quota.

> **TIP**: Standardizing on one named transformation is also what lets you take advantage of [baseline transformations](transformation_reference#bl_baseline), which avoid regenerating shared transformation steps.

## Step 3: Warm the cache with eager transformations
At this point the transformation is generated the first time someone requests it. To have it ready in advance, add an [eager transformation](eager_and_incoming_transformations#eager_transformations) to the [upload presets](upload_presets) used by the images that need it, so the derived image is generated at upload time rather than on first view.

### Match the delivery URL exactly

An eager transformation only warms the cache if it produces the same derived image your delivery URL asks for. Any difference, including the order of the parameters, results in a separate derived image and a cold cache for the URL you're actually serving. For example, if you eagerly generate `c_fill,h_400,w_600`, your delivery URL has to use that same order, not `c_fill,w_600,h_400`. See [Test before production](upload_presets#test_before_production).

`q_auto` goes in the eager transformation as its own component, exactly as it appears in your delivery URL, and so does any quality applied by default. The one thing that can't match is `f_auto`. Because there's no requesting browser at upload time, `f_auto` has no effect in an eager transformation, so instead you eagerly generate the specific formats that `f_auto` will later serve. See [Using automatic format in eager transformations](eager_and_incoming_transformations#using_automatic_format_in_eager_transformations), and [Don't use f_auto, use specific formats instead](upload_presets#don_39_t_use_f_auto_use_specific_formats_instead) for the upload preset specifics.

When you set an explicit format in an SDK or API call, also set the `format` parameter to an empty string. Otherwise the generated URL picks up a file extension as well as the `f` parameter, and no single `f_auto` delivery URL can match every eagerly generated variant.

For images, eagerly generate the formats `f_auto` actually delivers to your audience:

* `f_webp` (WebP), selected for the vast majority of modern browsers
* `f_jpg` (JPEG), or `f_png` for images that need transparency, as the fallback
* `f_avif` (AVIF), which gives the best compression for the browsers that support it

> **NOTE**: Whether `f_auto` can deliver AVIF depends on your account plan's metric. If your plan uses the [image impressions metric](developer_onboarding_faq_image_impressions), AVIF is supported automatically. If it uses the **image bandwidth metric**, AVIF isn't supported for `f_auto` by default; on an [Enterprise plan](https://cloudinary.com/pricing#pricing-enterprise) you can discuss options with your Cloudinary Customer Success Manager. See [f_auto format availability](image_optimization#fauto_format_availability).

There's no point eagerly generating a format `f_auto` won't serve, so only include AVIF if it's available on your plan. Each extra format is another derived image per asset.

The examples below generate the first two, which covers most delivery scenarios.
### Add the eager transformation to your upload presets

Update the upload presets used by the images that need this transformation, so that each new upload eagerly generates it. The eager value references the named transformation, so you still only maintain the transformation itself in one place.

If you're setting the eager transformation in the Console under [Settings > Upload > Upload presets](https://console.cloudinary.com/app/settings/upload/presets), the field takes URL syntax rather than SDK parameters, and you add each transformation separately using the **+** button. Chain the components within one transformation with a slash (`/`), so the first two are entered as:

```
t_product_card/f_webp/q_auto
```

```
t_product_card/f_jpg/q_auto
```

If your transformations take a while to generate, turn on the **Apply eager transformations asynchronously** toggle below the transformation fields, so uploads aren't held up while the derived images are created.

Using the Admin API or an SDK instead:

```multi
|nodejs
cloudinary.v2.api.update_upload_preset("product_images",
  { eager: [
    { format: "", transformation: [
      { transformation: "product_card" }, { fetch_format: "webp" }, { quality: "auto" }] },
    { format: "", transformation: [
      { transformation: "product_card" }, { fetch_format: "jpg" }, { quality: "auto" }] }] })
.then(result => console.log(result));

|python
cloudinary.api.update_upload_preset("product_images",
  eager = [
    {"format": "", "transformation": [
      {"transformation": "product_card"}, {"fetch_format": "webp"}, {"quality": "auto"}]},
    {"format": "", "transformation": [
      {"transformation": "product_card"}, {"fetch_format": "jpg"}, {"quality": "auto"}]}])

|ruby
Cloudinary::Api.update_upload_preset("product_images",
  eager: [
    {format: "", transformation: [
      {transformation: "product_card"}, {fetch_format: "webp"}, {quality: "auto"}]},
    {format: "", transformation: [
      {transformation: "product_card"}, {fetch_format: "jpg"}, {quality: "auto"}]}])

|php_2
$api->updateUploadPreset("product_images", [
  "eager" => [
    ["format" => "", "transformation" => [
      ["transformation" => "product_card"], ["fetch_format" => "webp"], ["quality" => "auto"]]],
    ["format" => "", "transformation" => [
      ["transformation" => "product_card"], ["fetch_format" => "jpg"], ["quality" => "auto"]]]]]);

|java
api.updateUploadPreset("product_images", ObjectUtils.asMap(
  "eager", Arrays.asList(
      new EagerTransformation().named("product_card").fetchFormat("webp").quality("auto").format(""),
      new EagerTransformation().named("product_card").fetchFormat("jpg").quality("auto").format(""))));

|csharp
var presetParams = new UploadPresetParams(){
  Name = "product_images",
  Eager = new List<Transformation>(){
      new EagerTransformation().Named("product_card").FetchFormat("webp").Quality("auto").Format(""),
      new EagerTransformation().Named("product_card").FetchFormat("jpg").Quality("auto").Format("")}};
cloudinary.UpdateUploadPreset(presetParams);

|go
resp, err := cld.Admin.UpdateUploadPreset(ctx, admin.UpdateUploadPresetParams{
  Name: "product_images",
  Eager: "t_product_card/f_webp/q_auto|t_product_card/f_jpg/q_auto"})

|curl
curl \
  -d 'eager=t_product_card/f_webp/q_auto|t_product_card/f_jpg/q_auto' \
  -X PUT \
  https://<API_KEY>:<API_SECRET>@api.cloudinary.com/v1_1/<CLOUD_NAME>/upload_presets/product_images

|cli
cld admin update_upload_preset product_images eager='[{"transformation": "product_card", "fetch_format": "webp", "quality": "auto", "format": ""}, {"transformation": "product_card", "fetch_format": "jpg", "quality": "auto", "format": ""}]'
```
You can also ask an agent connected to the [Environment Config MCP server](cloudinary_llm_mcp#available_mcp_servers) to update all the relevant presets for you, which saves writing a loop when you have many of them.

For the full set of options, see [Create an upload preset](admin_api#create_an_upload_preset) and [Update an upload preset](admin_api#update_an_upload_preset) in the _Admin API Reference_, and [Managing upload presets](upload_presets#creating_and_managing_upload_presets), including the [best practices for upload presets](upload_presets#best_practices_for_upload_presets).

### Warm the cache for images you've already uploaded

Upload presets only affect new uploads. To generate the same derived images for images already in your product environment, use the [explicit](image_upload_api_reference#explicit) method:

```multi
|nodejs
cloudinary.v2.uploader.explicit("product-photo",
  { type: "upload",
    eager: [
      { transformation: "product_card", fetch_format: "webp", quality: "auto", format: "" },
      { transformation: "product_card", fetch_format: "jpg", quality: "auto", format: "" }] })
.then(result => console.log(result));

|python
cloudinary.uploader.explicit("product-photo",
  type = "upload",
  eager = [
    {"format": "", "transformation": [
      {"transformation": "product_card"}, {"fetch_format": "webp"}, {"quality": "auto"}]},
    {"format": "", "transformation": [
      {"transformation": "product_card"}, {"fetch_format": "jpg"}, {"quality": "auto"}]}])

|ruby
Cloudinary::Uploader.explicit("product-photo",
  type: "upload",
  eager: [
    {format: "", transformation: [
      {transformation: "product_card"}, {fetch_format: "webp"}, {quality: "auto"}]},
    {format: "", transformation: [
      {transformation: "product_card"}, {fetch_format: "jpg"}, {quality: "auto"}]}])

|php_2
$cloudinary->uploadApi()->explicit("product-photo", [
  "type" => "upload",
  "eager" => [
    ["format" => "", "transformation" => [
      ["transformation" => "product_card"], ["fetch_format" => "webp"], ["quality" => "auto"]]],
    ["format" => "", "transformation" => [
      ["transformation" => "product_card"], ["fetch_format" => "jpg"], ["quality" => "auto"]]]]]);

|java
result = cloudinary.uploader().explicit("product-photo",
  ObjectUtils.asMap(
    "type", "upload",
    "eager", Arrays.asList(
      new EagerTransformation().named("product_card").fetchFormat("webp").quality("auto").format(""),
      new EagerTransformation().named("product_card").fetchFormat("jpg").quality("auto").format(""))));

|csharp
var explicitParams = new ExplicitParams("product-photo"){
  Type = "upload",
  EagerTransforms = new List<Transformation>(){
      new EagerTransformation().Named("product_card").FetchFormat("webp").Quality("auto").Format(""),
      new EagerTransformation().Named("product_card").FetchFormat("jpg").Quality("auto").Format("")}};
var explicitResult = cloudinary.Explicit(explicitParams);

|go
resp, err := cld.Upload.Explicit(ctx, uploader.ExplicitParams{
  PublicID: "product-photo",
  Type: "upload",
  Eager: "t_product_card/f_webp/q_auto|t_product_card/f_jpg/q_auto"})

|curl
curl https://api.cloudinary.com/v1_1/<CLOUD_NAME>/image/explicit -X POST \
  --data 'type=upload&public_id=product-photo&eager=t_product_card/f_webp/q_auto|t_product_card/f_jpg/q_auto&timestamp=173719931&api_key=<API_KEY>&signature=<SIGNATURE>'

|cli
cld uploader explicit product-photo type=upload eager='[{"transformation": "product_card", "fetch_format": "webp", "quality": "auto", "format": ""}, {"transformation": "product_card", "fetch_format": "jpg", "quality": "auto", "format": ""}]'
```
Run this over the relevant images, for example by listing them with the [Admin API](admin_api#get_resources) or by [searching](search_method) for the tag or folder that identifies them.

> **NOTE**: An `explicit` call is processed, and counted against your transformation quota, even when an identical derived image already exists. Scope the list you run it over to the images that actually need it.

> **TIP**: Eager generation adds time to the upload request. Set `eager_async` to `true` and supply an `eager_notification_url` to be notified when the derived images are ready. See [Eager asynchronous transformations](eager_and_incoming_transformations#eager_asynchronous_transformations).

## Checklist

Before you consider the rollout done, confirm that:

* The named transformation contains the full transformation, without `f_auto` or `q_auto`.
* Every delivery URL and SDK call in your app references the named transformation, chained with `f_auto/q_auto`.
* The eager transformation in your upload presets matches the delivery transformation exactly, including parameter order, with explicit formats in place of `f_auto`.
* You've run `explicit` over your existing images, so their derived versions already exist.
* If you changed an existing named transformation, you've invalidated and regenerated the affected derived images.

> **READING**:
>
> Related topics:

> * [Named transformations](named_transformations)

> * [Eager and incoming transformations](eager_and_incoming_transformations)

> * [Managing upload presets](upload_presets)

> * [Optimize images](image_optimization)

> * [Roll out a video transformation](video_transformation_rollout)
