Image Transforms

Instead of requiring content editors to upload images at a specific sizes, Craft lets you define “image transforms” for automatically manipulating images in predefined ways, or on-demand. Transforms are non-destructive, meaning they have no effect on the original uploaded image.

Transforms can be defined in the control panel or directly from your templates and GraphQL queries.

# Named Transforms

Named transforms are created from the control panel by navigating to SettingsAssetsImage Transforms and press New Transform.

my-craft-project.ddev.site/admin/settings/assets/transforms/new
Asset transform edit screen
Creating a new predefined asset transform in the control panel.

Every transform has the following settings:

Name

A user-facing name or label for the transform. This is mostly for your reference, but it may be exposed to content authors when using Redactor (opens new window).

Handle

A template-facing handle, used when accessing transforms in Twig or via GraphQL. Changing this may require updates to your templates!

Mode

Specifies how the transform is handled:

  • Crop (Default) — Crops the image to the specified width and height.
  • Fit — Scales the image so that it is as big as possible with all dimensions fitting within the specified widthand height.
  • Letterbox — Stretches the image to the specified width and height.
  • Stretch — Stretches the image to the specified width and height.

All modes support some general controls over image size and quality:

Width

Final transformed image width, or blank to calculate automatically from the original image’s aspect ratio.

Height

Final transformed image height, or blank to calculate automatically from the original image’s aspect ratio.

Allow Upscaling

Whether or not Craft is allowed to scale an image beyond its original dimensions. Affects the maximum Width and Height for Fit, Crop, and Stretch output, and the matting strategy for Letterbox transforms.

Quality

Sets a quality or compression ratio for the transformed image, depending on the format of the target image. When left blank, the quality will be determined by the defaultImageQuality config setting.

Interlacing

Specify an interlacing (opens new window) strategy for the pixels in raster images.

Image Format

Format for the transformed image.

  • Auto (same as source image, if web-safe (opens new window), otherwise jpg)
  • jpg
  • png
  • gif
  • webp (when supported by ImageMagick)
  • avif (when supported by ImageMagick)

The transformGifs setting allows you to completely disable transformation of GIF images. Animated GIFs often consume significant resources to resize, and rarely produce well-optimized output.

You do not need to update templates for this setting to work—Craft will just ignore the transform and return a URL to the original image.

Some modes may behave the same when using only one of the Width and Height settings. Typically, an unset dimension means the image’s aspect ratio is maintained.

Suppose we have a source image that is 600 pixels wide and 400 pixels tall. Applying a Stretch transform that only declares a Width of 60 pixels would produce an image 40 pixels tall.

# Transform Modes

Additional settings are available for some transform modes.

# Crop

When using the Crop mode, a Default Focal Point setting is revealed, allowing customization of which image area Craft should center the crop on when a focal point isn’t specified. Its options include:

Label Handle
Top-Left top-left
Top-Center top-center
Top-Right top-right
Center-Left center-left
Center-Center center-center
Center-Right center-right
Bottom-Left bottom-left
Bottom-Center bottom-center
Bottom-Right bottom-right

When a focal point is known for an image, Craft will attempt to keep that point as close to the center of the crop as possible.

# Fit

The Fit mode always respects the original image’s aspect ratio. The image is scaled to fit within the specified Width and/or Height, but may not cover the entire allowed area. Focal points have no effect when using this mode, as images are never cropped.

# Letterbox

Two new options are revealed when using the Letterbox mode:

  • Fill Color — A hexadecimal color value used for the matte. When supported, the default matte is transparent; otherwise, white.
  • Image Position — Where the image will be anchored, when it does not fill the entire target frame. The same anchoring options are available here as for the Default Focal Point option in the Crop and Fit modes.

Letterbox mode preserves images’ aspect ratios, fitting them within the provided width and height and filling the remaining space with the Fill Color. If the target frame is larger than the original image’s dimensions and the Allow Upscaling option is Off, Craft may matte the image on more than two sides.

# Stretch

Like Crop and Fit, Stretch is only differentiated from other transforms when used with a Height and a Width setting.

With both set, the image is scaled horizontally and vertically to the target size.

As the name of this mode suggests, using a target height and width that are a different aspect ratio than the original image will cause the image to be stretched disproportionately.

# Applying Named Transforms to Images

To output an image with your named transform applied, pass its handle into your asset’s getImg() (opens new window), getUrl() (opens new window), getWidth() (opens new window), and getHeight() (opens new window) functions:

<img src="{{ asset.getUrl('thumb') }}"
  width="{{ asset.getWidth('thumb') }}"
  height="{{ asset.getHeight('thumb') }}">

You can also apply the transform on the asset so any relevant properties are automatically manipulated by default. This example would output the same result as the example above:

{% do asset.setTransform('thumb') %}
<img src="{{ asset.url }}"
  width="{{ asset.width }}"
  height="{{ asset.height }}">

# Defining Transforms in your Templates

You can also define transforms directly in your templates by creating a hash with the desired parameters:

{% set thumb = {
  mode: 'crop',
  width: 100,
  height: 100,
  quality: 75,
  position: 'top-center'
} %}

See a list of all the possible keys and values for this object.

Then pass that hash into your asset’s getUrl(), getWidth(), and getHeight() functions:

<img src="{{ asset.getUrl(thumb) }}"
  width="{{ asset.getWidth(thumb) }}"
  height="{{ asset.getHeight(thumb) }}">

Notice in this example there are no quotes around “thumb” like there were in our earlier examples. That’s because before we were passing a string set to reference a control-panel-defined transform by its handle, whereas this example passes a variable containing the thumb options hash we created in the template.

It would look similar using setTransform() like we did in the previous section:

{% do asset.setTransform(thumb) %}
<img src="{{ asset.url }}"
  width="{{ asset.width }}"
  height="{{ asset.height }}">

# Possible Values

All the same settings available to named transforms are available to template-defined transforms and transforms requested via GraphQL.

  • The mode property can be set to either 'crop', 'fit', 'letterbox', or 'stretch'.
  • width and height can each be set to an integer, or omitted.
  • quality can be set to a number between 0 and 100, or omitted to use the defaultImageQuality setting.
  • format can be set to 'jpg', 'gif', 'png', 'webp', 'avif', or omitted.
  • A position property (set to one of the valid values listed above) is supported when mode is set to 'crop' or 'letterbox'. The behavior is different for each type of transform.

# Overriding Named Transforms

Should you need to break from a named transform in a few places, you can specify overrides alongside a transform key:


 
 







{% do asset.setTransform({
  transform: 'thumb',
  quality: 100,
}) %}

<img
  src="{{ asset.url }}"
  width="{{ asset.width }}"
  height="{{ asset.height }}">

This would accomplish the same thing as the example before it, except quality would be maxed out at 100 rather than whatever was set on the named 'thumb' transform.

You can override settings like this with the getUrl() method, too:

<source type="image/webp" srcset="{{ asset.getUrl({
  transform: 'thumb',
  format: 'webp',
}) }}">

This would use the named 'thumb' transform’s settings, but always generate a .webp file.

# Generating srcset Sizes

Transforms are a great way to avoid serving unnecessarily-large images to your users—but there’s still room for optimization! Most browsers support the srcset (opens new window) attribute, which allows you to define a collection of images that are appropriate for a given device or viewport.

Rather than creating an exhaustive list of named transforms or building up multiple config hashes in your templates, you can offload this work to Craft with the getSrcSet() (opens new window) method. Here’s an example that uses the tag template function to render an image with a srcset attribute containing three variations based on a single transform:

{% do asset.setTransform({ width: 300, height: 300 }) %}
{{ tag('img', {
  src: asset.url,
  width: asset.width,
  height: asset.height,
  srcset: asset.getSrcset(['1.5x', '2x', '3x']),
  alt: asset.title,
}) }}

This can be done even more succinctly by passing a second sizes argument to the getImg() (opens new window) method:

{{ asset.getImg({ width: 300, height: 300 }, ['1.5x', '2x', '3x']) }}

Don’t forget to configure the corresponding sizes attribute, as well—Craft can manage the transforms and build a valid srcset, but it doesn’t know how the images are actually used in your front-end!

You can also provide relative image sizes when eager-loading asset transforms. See AssetQuery::withTransforms() (opens new window) in the class reference for an example.

# GraphQL

When requesting asset data via the GraphQL API, you can get named and ad-hoc transforms with the @transform directive.

Like we’ve seen in the template-defined transforms, the GraphQL API allows you to retrieve individual properties as though they were tied to a transform…

query Thumbnails {
  assets(volume: "images") {
    title
    alt
    url @transform(width: 640, mode: "fit")
    width
    height
  }
}

…or set a transform on the entire asset object:

query Gallery {
  assets(volume: "images") @transform("fullscreen") {
    title
    alt
    url
    width
    height
  }
}

Note that in the first example, the height and width values are for the original image, whereas in the second example, they reflect the transformed dimensions.