Display Modes and Image Styles

This content was generated with the assistance of AI and should be verified against the official Drupal documentation before being relied on in production.

This sub-section’s Drupal bibliography lists the reference material consulted while preparing these pages.

Two configuration systems decide what a visitor and an editor see for every entity on a Drupal site. Display modes (view modes and form modes) decide which fields appear, in which order and through which formatter or widget. Image styles decide how an uploaded picture is turned into the files that a browser really downloads. They are both configuration entities, they are both edited in the administration interface, and they are both exported with configuration management. This page was checked against the official documentation on 2026-10-30 and is written for Drupal 11.4.x with PHP 8.3 or later. Nothing described here is among the modules or features that leave core in Drupal 12 (scheduled for the week of 2026-12-07), and the Drupal 10 line reaches its end of life on 2026-12-09, so new sites should not start on it.

The running example is the Meridian Museum. Its exhibition content type is shown as a full page, as a card in a listing and as a hero banner on the home page; curators get a trimmed editing form of their own; and every photograph must reach visitors as a small WebP or AVIF file instead of the 6000-pixel original that a photographer uploaded. The related pages are Content types and fields, Field types and entity references, Media and the Media Library, Breakpoints and responsive images, Twig templates and debugging and Performance and caching.

Part 1: View modes and form modes

Entities, bundles and displays

Every content entity type (node, media, user, comment, taxonomy term, block content and your own types) can be shown in several contexts. A view mode is the name of such a context: full for the page of the entity itself, teaser for a short version in a listing, search_index and search_result for search, rss for feeds and token for token replacement. The User Guide defines a view mode as "the context in which an entity …​ is displayed" and a formatter as the rule that shows a field’s values. For each combination of entity type, bundle and view mode there is one entity view display, a configuration entity named core.entity_view_display.<entity_type>.<bundle>.<view_mode>. It stores, for each field, whether it is shown, its weight, its label position and the formatter with its settings.

Form modes mirror this for editing. A form mode is a named variant of the entity edit form. The default form mode is called default; the user entity also has a register form mode used by the account creation form. Each combination of entity type, bundle and form mode has an entity form display, core.entity_form_display.<entity_type>.<bundle>.<form_mode>, that stores, for each field, the widget, its settings and its weight. The pieces fit together as follows.

flowchart TD ET[Entity type: node] --> B[Bundle: exhibition] B --> VM1[View mode: full] B --> VM2[View mode: teaser] B --> VM3[View mode: card - custom] B --> FM1[Form mode: default] B --> FM2[Form mode: curator - custom] VM3 --> EVD[Entity view display<br/>core.entity_view_display.node.exhibition.card] FM2 --> EFD[Entity form display<br/>core.entity_form_display.node.exhibition.curator] EVD --> F1[Field + formatter + settings] EFD --> F2[Field + widget + settings]

The view mode and form mode definitions themselves are configuration entities too: core.entity_view_mode.<entity_type>.<mode> and core.entity_form_mode.<entity_type>.<mode>. A mode definition has only an ID, a label, a target entity type and a status; everything useful lives in the displays.

Screens and paths

  • Structure > Content types > Manage display (/admin/structure/types/manage/exhibition/display) edits the view displays of one bundle. The Default tab is the fallback; further tabs appear for each view mode the bundle uses.

  • Structure > Content types > Manage form display (/admin/structure/types/manage/exhibition/form-display) edits the form displays.

  • Structure > Display modes > View modes (/admin/structure/display-modes/view) and Form modes (/admin/structure/display-modes/form) create and delete the modes themselves.

  • The same Manage display tabs exist for media types, taxonomy vocabularies, users and block types.

Creating a mode does not make it available everywhere. At the bottom of Manage display, the Custom display settings fieldset lists the view modes defined for the entity type, and a bundle only has its own display for a mode that you tick there. This is the single most common surprise: a custom card mode that was never enabled for exhibition is rendered with the default display, so an apparently changed configuration changes nothing.

The default display and the fallback chain

When code asks for an entity in a view mode, Drupal loads the display <type>.<bundle>.<mode>. If that display does not exist or is not enabled, it falls back to <type>.<bundle>.default. This is why teaser and full work on a new bundle without any configuration: both resolve to default until you enable Custom display settings for them. Teasers usually differ from the full page in three ways: the body is shown through the Summary or trimmed formatter, the image uses a smaller image style, and secondary fields such as the opening hours are hidden.

# config/sync/core.entity_view_mode.node.card.yml
langcode: en
status: true
dependencies:
  module:
    - node
id: node.card
label: Card
description: 'Compact card used in exhibition listings.'
targetEntityType: node
cache: true
# config/sync/core.entity_view_display.node.exhibition.card.yml (abridged)
langcode: en
status: true
dependencies:
  config:
    - core.entity_view_mode.node.card
    - field.field.node.exhibition.field_hero_image
    - field.field.node.exhibition.field_summary
    - image.style.meridian_card_4x3
    - node.type.exhibition
  module:
    - image
    - text
    - user
id: node.exhibition.card
targetEntityType: node
bundle: exhibition
mode: card
content:
  field_hero_image:
    type: image
    label: hidden
    settings:
      image_link: content
      image_style: meridian_card_4x3
      image_loading:
        attribute: lazy
    third_party_settings: {  }
    weight: 0
    region: content
  field_summary:
    type: text_trimmed
    label: hidden
    settings:
      trim_length: 160
    third_party_settings: {  }
    weight: 1
    region: content
hidden:
  field_gallery: true
  links: true
  field_related_objects: true

The dependencies block is generated for you. It shows why an image style cannot be deleted while a display uses it: the display depends on image.style.meridian_card_4x3, and the configuration system refuses to delete a dependency that is still in use (the delete form lists the dependent displays). Removing a style by editing exported YAML and importing it causes the same error at import time, which is why a style is removed in a separate deployment after the displays have stopped using it.

Choosing a view mode in each place that renders content

Where the view mode comes from depends on who renders the entity.

Context How the view mode is chosen

Node page (/node/12)

Always full.

Views, with Show: Content

A Row style setting in the view; the view exhibitions_listing uses card.

Entity reference field with Rendered entity formatter

The View mode setting of the formatter.

Layout Builder block "Content"

The view mode of the section’s default display.

Media embedded in CKEditor 5

The Default view mode of the Embed media filter, per embed button.

Custom code

The argument to EntityViewBuilderInterface::view() or viewMultiple().

Rendering a referenced entity as a full node inside a listing is the most common performance mistake of site builders: it loads and renders every field of every referenced entity. Prefer a light mode (teaser, card) for references and reserve full for the entity’s own page. See Field types and entity references for the loop-protection behaviour of rendered entities.

Rendering a view mode in code

<?php

declare(strict_types=1);

namespace Drupal\meridian_collection\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Renders the three most recent exhibitions as cards.
 */
final class FeaturedExhibitionsController extends ControllerBase {

  public function __construct(
    private readonly EntityTypeManagerInterface $manager,
  ) {}

  public static function create(ContainerInterface $container): static {
    return new static($container->get('entity_type.manager'));
  }

  /**
   * @return array<string, mixed>
   */
  public function content(): array {
    $storage = $this->manager->getStorage('node');
    $ids = $storage->getQuery()
      ->accessCheck(TRUE)
      ->condition('type', 'exhibition')
      ->condition('status', 1)
      ->sort('created', 'DESC')
      ->range(0, 3)
      ->execute();

    $nodes = $storage->loadMultiple($ids);
    // viewMultiple() renders every node in one pass, which lets the
    // formatters preload referenced entities.
    return $this->manager->getViewBuilder('node')->viewMultiple($nodes, 'card');
  }

}

The query calls accessCheck() explicitly because entity queries no longer assume it. The returned render array carries cache tags (node:12, config:core.entity_view_display.node.exhibition.card) and the languages and user.permissions contexts, so editing either the node or the display invalidates exactly the pages that used it.

A hook can also swap the mode just before rendering. Since Drupal 11.1 hooks are methods with the #[Hook] attribute on autowired classes in src/Hook, and the method for hook_entity_view_mode_alter is registered like this.

<?php

declare(strict_types=1);

namespace Drupal\meridian_collection\Hook;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Hook\Attribute\Hook;

/**
 * Uses the compact card for objects that are not on display.
 */
final class ViewModeHooks {

  #[Hook('entity_view_mode_alter')]
  public function alterViewMode(string &$view_mode, EntityInterface $entity): void {
    if ($entity->getEntityTypeId() === 'collection_object'
      && $view_mode === 'teaser'
      && !$entity->get('on_display')->value) {
      $view_mode = 'card';
    }
  }

}

Modules that must run on Drupal 10.x as well add a #[LegacyHook] procedural wrapper; a Drupal 11-only module needs nothing else. Hooks for installation and updates stay procedural.

Form modes in practice

The Meridian curators edit exhibitions on a phone in the gallery, and the full editing form with 25 fields is too much for them. A custom form mode curator shows only the title, the hero image, the on_display flag and the summary.

# config/sync/core.entity_form_mode.node.curator.yml
langcode: en
status: true
dependencies:
  module:
    - node
id: node.curator
label: Curator
targetEntityType: node

Source: Entity API

The mode is useless until code selects it. A route can ask the entity form builder for it through the _entity_form route default, written as <entity_type>.<operation>. The operation is the form mode name.

# meridian_collection.routing.yml (excerpt)
meridian_collection.curator_edit:
  path: '/curator/exhibition/{node}/edit'
  defaults:
    _entity_form: 'node.curator'
    _title: 'Quick edit exhibition'
  requirements:
    _entity_access: 'node.update'
  options:
    parameters:
      node:
        type: entity:node

Three details are worth knowing. First, when an entity type has no form handler named after the mode, the default handler class is used and only the form display changes, which is what happens for nodes. Second, the route performs the entity access check but nothing here relaxes field-level access: a field hidden in a form mode is still validated and saved with its previous value, and a field the user may not edit is not shown at all. Third, hiding a required field in a form mode makes the form impossible to submit if the field has no default value, so required fields stay in every form mode that can save the entity. A form mode is a presentation tool, not a permission mechanism.

For content-moderation sites, the transition buttons appear in whichever form mode shows the Moderation control pseudo-field, so keep it in curator too. See Content moderation, workflows and revisions.

View modes in Twig and template suggestions

A template can find out which view mode it is rendering: in node.html.twig the variable view_mode holds it, and the theme system offers the suggestion node--exhibition--card.html.twig as well as node--card.html.twig. The Meridian theme uses this to wrap the card in its exhibition-teaser component.

{# templates/content/node--exhibition--card.html.twig #}
{% set classes = ['exhibition-card', node.isPromoted() ? 'is-featured'] %}
<article{{ attributes.addClass(classes) }}>
  {{ content.field_hero_image }}
  <h3{{ title_attributes }}>
    <a href="{{ url }}" rel="bookmark">{{ label }}</a>
  </h3>
  {{ content.field_summary }}
</article>

Template suggestions, preprocess functions and how to find the right template with Twig debugging are covered in Template suggestions and preprocess and Twig templates and debugging. The cache behaviour of a render array is covered in Render API and cache metadata.

Performance and cache notes for view modes

  • The cache: true key of a view mode definition lets the entity view builder cache the rendered entity. Turn it off only for a mode whose output depends on something the cache contexts cannot express.

  • Each rendered entity in a reference field adds a cache tag of the referenced entity, so editing a referenced object invalidates every page that embeds it. That is correct, and it is also why a heavily shared entity should use a light mode.

  • Layout Builder can override the display of a single entity (a per-entity layout). The override is stored on the entity, so later changes to the display configuration do not reach overridden entities. See Layout Builder.

Part 2: Image styles

Why image styles exist

An editor uploads a 7 MB photograph; the card shows it in a 400-pixel box. Shipping the original wastes bandwidth and ruins the page speed score, and resizing by CSS does not reduce the download. An image style is a named, ordered list of image effects that Drupal applies to the original to produce a derivative. The original is never modified, the derivative is created on the first request that needs it, and the result is cached on disk. Changing a style flushes its derivatives, which are then regenerated on demand.

Image styles live in the core image module. The configuration entity type is image_style, with configuration names image.style.<machine_name>, and it exports the name, label and effects keys. The administration screen is Configuration > Media > Image styles (/admin/config/media/image-styles) and the permission that guards it is administer image styles.

Core effects

Effect What it does

Scale

Resizes keeping the aspect ratio so the result fits inside the width and height. Leaving one dimension empty keeps the ratio from the other. An Allow upscaling option decides whether small originals are enlarged.

Scale and crop

Resizes to cover the target box and crops the overflow around an anchor point. The result always has exactly the requested dimensions, so it is the usual choice for cards.

Resize

Forces exact dimensions without keeping the ratio. It distorts the picture and is rarely right.

Crop

Cuts a rectangle of a given size from an anchor position without scaling.

Rotate

Rotates by an angle, with an optional random mode and a background colour for the corners.

Desaturate

Converts to greyscale.

Convert

Changes the file format, for example from JPEG or PNG to WebP or AVIF. See the next section.

Effects run in the order of their weights and each one receives the output of the previous one, so a Convert effect is usually last. The Meridian meridian_card_4x3 style is a Scale and crop to 640 by 480 pixels followed by a Convert to WebP.

Contrib modules add effects (focal point cropping, image-style-specific watermarking and so on), and a module can define its own effect as a plugin in src/Plugin/ImageEffect. Core effects extend ConfigurableImageEffectBase and use the #[ImageEffect] attribute, for example the image_convert plugin of class Drupal\image\Plugin\ImageEffect\ConvertImageEffect.

Creating a style by hand and as configuration

The User Guide walks through the administration screen: add the style, add an effect, set the dimensions, then choose the style in the formatter settings of Manage display. The same style as exported configuration looks like this.

# config/sync/image.style.meridian_card_4x3.yml
langcode: en
status: true
dependencies: {  }
name: meridian_card_4x3
label: 'Card 4:3 (640x480)'
effects:
  3b9d5a10-7c2f-4f0a-9d1e-6a5b3c2d1e01:
    uuid: 3b9d5a10-7c2f-4f0a-9d1e-6a5b3c2d1e01
    id: image_scale_and_crop
    weight: 1
    data:
      width: 640
      height: 480
      anchor: center-center
  8c1e4b22-5d3a-4e7b-a2f6-9b0c4d5e6f02:
    uuid: 8c1e4b22-5d3a-4e7b-a2f6-9b0c4d5e6f02
    id: image_convert
    weight: 2
    data:
      extension: webp

Use descriptive machine names that say what the style is for (meridian_card_4x3, meridian_hero_wide), not the current size. A style named medium_300 that later becomes 480 pixels wide misleads everyone. Core installs the styles thumbnail, medium, large and wide (the exact set depends on the install profile), and Media, the Media Library and the user picture refer to some of them by name, so do not delete styles that you did not create.

Where the files go: derivatives and the styles/ directory

A derivative is an ordinary file stored beside the originals. For the public file system the layout is as follows.

web/sites/default/files/
  2026-10/hero-gallery.jpg                              <- original (public://2026-10/hero-gallery.jpg)
  styles/
    meridian_card_4x3/
      public/
        2026-10/
          hero-gallery.jpg.webp                         <- derivative
    large/
      public/
        2026-10/
          hero-gallery.jpg

The path is styles/<style>/<scheme>/<path of the original>. For the private file system the scheme directory is private, and the files sit below the private directory, outside the web root. When the Convert effect changes the extension, the new extension is appended to the original file name (hero-gallery.jpg.webp), so two originals that differ only in extension never collide. The URL of the derivative contains an itok query parameter, for example /sites/default/files/styles/meridian_card_4x3/public/2026-10/hero-gallery.jpg.webp?itok=Q7kZ3fXa.

The first request for a URL that does not exist yet does not match a real file, so the web server hands it to Drupal’s image style download route. Drupal checks the token, locks the derivative so that parallel requests do not generate it twice, applies the effects to the original, writes the file and streams it. Every later request is served by the web server straight from disk without starting PHP.

sequenceDiagram participant B as Browser participant W as Web server participant D as Drupal image style route participant F as Files directory B->>W: GET styles/meridian_card_4x3/public/hero.jpg.webp?itok=... W->>F: file exists? alt derivative already generated F-->>W: file W-->>B: 200 image, no PHP else first request W->>D: rewrite to index.php D->>D: validate itok, check style and source D->>F: read original hero.jpg D->>D: apply effects in weight order D->>F: write derivative D-->>B: 200 image, generated now end

This design has consequences that explain most support questions.

  • The web server must route missing files under styles/ to Drupal. The default Apache .htaccess and the documented nginx location blocks do. A hand-written nginx configuration that answers 404 for any missing file under sites/*/files produces broken images for every derivative that has not been generated yet. See Requirements and hosting.

  • The token protects against abuse. Without a token anyone could request thousands of arbitrary derivatives and fill the disk. The itok value is derived from the style, the file and the private site key, so a visitor cannot invent new derivative URLs. Two settings in image.settings relax this: allow_insecure_derivatives lets tokenless requests succeed, and suppress_itok_output removes the token from generated URLs. Leave both at false. Section "Image style token and derivative security" below shows how to confirm them.

  • Derivatives for the private scheme go through Drupal each time, because the web server cannot serve files outside the web root. They honour file access hooks and are an application-level cost. See File system and private files.

  • In a cluster, every web node needs the same files directory. A derivative written to one node’s local disk is invisible to its neighbours, which then regenerate their own. Use a shared volume or object storage. See Deployment and CI/CD.

  • Generating derivatives needs memory. GD decompresses the original into raw pixels, so a 24-megapixel photograph needs roughly 100 MB of PHP memory regardless of its file size. A "memory exhausted" error at the first request is the usual symptom, and the fix is memory_limit, not a different style.

Flushing and warming

Changing or saving a style flushes all its derivatives, and the Flush operation on the style list does so on demand. From the command line, Drush provides image:flush (alias if).

# flush the derivatives of two styles
ddev drush image:flush meridian_card_4x3,meridian_hero_wide

# flush every derivative; they are regenerated on demand
ddev drush image:flush --all

Flushing all styles of a large museum collection makes the next visitors pay the cost of generating thousands of derivatives at once. Flush selectively, and flush before a traffic peak rather than during it. The contrib Image Style Warmer module can create chosen derivatives when an image is uploaded or through a queue worker, so the first visitor does not wait.

Pages cache the URL of a derivative, not the file. After a style changes, the URL stays the same and the regenerated file replaces the old one, but browsers and CDNs that cached the old bytes keep serving them until their own lifetime ends. Purge the CDN as part of a deployment that changes a style.

Using a style from code

<?php

use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle;

// Build the URL of a derivative for a stored file (inside a service use
// the entity type manager instead of the static loaders).
$file = File::load($fid);
$style = ImageStyle::load('meridian_card_4x3');

if ($file !== NULL && $style !== NULL) {
  $url = $style->buildUrl($file->getFileUri());
  // The derivative exists only after the first request for $url, unless it
  // is created now:
  $style->createDerivative(
    $file->getFileUri(),
    $style->buildUri($file->getFileUri()),
  );
}

Source: API: ImageStyle

The methods that matter are buildUri() (the path a derivative will have), buildUrl() (the URL with the token), createDerivative() and flush(). When a template only has a file, prefer the Twig theme function: the image_style theme hook renders an img with width and height computed from the style, so the layout does not shift.

$build = [
  '#theme' => 'image_style',
  '#style_name' => 'meridian_card_4x3',
  '#uri' => 'public://2026-10/hero-gallery.jpg',
  '#alt' => 'Hands-on pendulum exhibit in the Motion gallery',
  '#attributes' => ['loading' => 'lazy'],
];

Image style token and derivative security

Image-derivative generation is a classic denial-of-service surface: each new URL costs CPU, memory and disk. The defaults protect you, and you should keep them. Check the two flags with Drush.

ddev drush config:get image.settings
# allow_insecure_derivatives: false
# suppress_itok_output: false

If a CDN or search crawler strips query strings and tokenless requests therefore fail, the correct answer is to make the CDN forward the query string for /styles/ paths and not to enable allow_insecure_derivatives. If you really need tokenless URLs (for example for a static site export), keep the set of styles small, limit what the web server accepts with rate limiting, and set the flags in settings.php for that one environment only.

// settings.php of a throwaway export environment only.
$config['image.settings']['suppress_itok_output'] = TRUE;

Also remember that an image style is not an access control mechanism: for public files the derivative is a public URL as soon as it has been requested. Files that must stay confidential belong in the private file system.

Uploads themselves are restricted by the field’s allowed extensions (png jpg jpeg webp is a good default; never svg for untrusted editors without sanitising it, and never php or html). Image styles only process raster formats that the toolkit supports; an SVG is served as it is.

Part 3: Format conversion, WebP and AVIF

The Convert effect

The Convert effect (plugin ID image_convert) changes the format of the derivative. Its Convert to list is built from the formats the active image toolkit supports, which is why the same site shows different choices in different environments. The default toolkit is GD, shipped with PHP; the contrib ImageMagick module provides an alternative toolkit that is often faster and supports more formats.

Format Notes

JPEG, PNG, GIF

Always available with GD.

WebP

Converting to WebP with the Convert effect has been supported by core for years. Needs GD with WebP support, which current PHP builds include. Typically 25 to 35 percent smaller than JPEG at equal visual quality.

AVIF

Supported by the core GD toolkit since Drupal 11.2, provided PHP’s GD extension is built on libgd 2.3.2 or later with libavif. Smaller than WebP, slower to encode.

The format list is the thing to check on every environment. Run php -r 'print_r(gd_info());' and look for the WebP Support and AVIF Support entries, or look at the Convert to list on a style’s effect form. A development container may lack AVIF while production has it, or the opposite.

AVIF with a fallback effect

Drupal 11.2.0 also added an effect named Convert to AVIF (with fallback). Because AVIF support depends on the server, AVIF cannot be the default for everyone. The effect creates AVIF derivatives where the toolkit supports the format, and falls back to another style, for example one that converts to WebP, where it does not.

The practical rule for Meridian is therefore:

  1. Create meridian_card_4x3_webp with Scale and crop followed by Convert to WebP. This style works everywhere.

  2. Create meridian_card_4x3 with Scale and crop followed by Convert to AVIF (with fallback), pointing at the WebP style.

  3. Use the AVIF style in formatters.

Whenever the toolkit lacks AVIF, the fallback turns the request into a WebP derivative and nobody sees a broken image. This answers a known pitfall of the plain approach: an image style that targets AVIF on a server without AVIF support may fail silently and leave editors with missing pictures. Related work on adding avif to the default allowed extensions of the standard image fields is still under discussion on drupal.org, so add it to your image field settings yourself if editors should upload AVIF originals.

Browser support and Accept negotiation

Drupal’s Convert effect writes one format per style and emits a single img URL. It does not negotiate the format with the Accept header. Every current mainstream browser displays WebP and AVIF, so one converted format is usually fine. When you need a guaranteed format for each browser, use a picture element whose source elements have different type attributes. That is done through responsive image styles, where each image style of a multiple-styles mapping can end in a different Convert effect. See Breakpoints and responsive images.

Older books and tutorials

Older books such as Drupal 10 Masterclass (2023) and Modernizing Drupal 10 Theme Development (2023) were written for Drupal 10.0 and 10.1, before the AVIF toolkit support of Drupal 11.2. They explain the Convert effect with JPEG, PNG and WebP only, and a tutorial written then does not offer the AVIF or fallback effects. The Drupal 10 line ends on 2026-12-09, and its last minor is 10.6, so these books are best read for the concepts (view modes, formatters, styles) and not for version-specific behaviour. Their descriptions of the administration screens for view modes and image styles remain recognisable in Drupal 11.

Part 4: Lazy loading

Browsers can postpone loading offscreen images when the img element has loading="lazy". The core Image formatter exposes this as a setting, image_loading, with the values lazy (the default) and eager. The setting form describes eager as the choice for images that are always displayed.

# in the formatter settings of a view display
type: image
settings:
  image_style: meridian_hero_wide
  image_link: ''
  image_loading:
    attribute: eager

The rule is one of Core Web Vitals. Mark the single image that is the largest element in the first viewport (the hero banner on the Meridian home page, which is the Largest Contentful Paint element) as eager, and leave every other image on lazy. Lazy loading the hero delays the largest paint, and eager loading a gallery of 60 thumbnails downloads them all on page load. The attribute is only a hint; it does not change the file size. Lazy images without width and height attributes cause layout shifts. The core formatter outputs the dimensions of the derivative, so keep them on the element and use CSS aspect-ratio or a fixed box in the theme where the markup is custom.

Other pieces rendered through other paths, such as images inside the body text, are handled differently: the CKEditor 5 image plugin stores a plain img in the text, and the core lazy-loading text filter (FilterImageLazyLoad), enabled per text format, adds loading="lazy" to images that carry width and height attributes and do not already say loading="eager". See Text formats and CKEditor 5.

Part 5: Responsive images

One image style produces one size. A responsive page needs several sizes of the same image, and the browser picks the right one. The core Responsive Image module maps breakpoints defined by the active theme to image styles and outputs either an img with srcset and sizes or a picture element. In Drupal it adds two things: a configuration entity, the responsive image style at Configuration > Media > Responsive image styles (/admin/config/media/responsive-image-style), and a field formatter called Responsive image, which replaces the Image formatter in Manage display. The formatter has the same image_loading setting as the Image formatter.

The use of a responsive style in the Meridian hero view mode is therefore only a matter of selecting a different formatter in the same screen described in Part 1; no template changes are needed.

# core.entity_view_display.node.exhibition.hero (excerpt)
content:
  field_hero_image:
    type: responsive_image
    label: hidden
    settings:
      responsive_image_style: meridian_hero
      image_link: ''
      image_loading:
        attribute: eager
    weight: 0
    region: content

Breakpoints are defined by a THEME.breakpoints.yml file, grouped and given resolution multipliers; the mapping of breakpoints to image styles, the choice between the srcset/sizes and picture approaches, art direction and the common mistakes of mapping are explained in Breakpoints and responsive images. Media items, the Media Library and image fields in Views use the same formatters, so a responsive style set once applies everywhere the formatter is used. See Media and the Media Library for the media view modes and the hero template override.

Common errors and pitfalls

Symptom Cause and fix

A new view mode changes nothing

The mode is not enabled in Custom display settings for the bundle, so the default display is used. Enable it and configure the display.

The field does not appear on the teaser

It is hidden in the teaser display, or the template prints fields one by one and omits it. Check Manage display and the template with Twig debugging.

Editing a display does not affect an exhibition

The node uses a Layout Builder override. Revert the override or reapply the layout.

Broken image links for new uploads, working ones for old

The web server does not hand missing files under styles/ to Drupal. Fix the rewrite or try_files rule.

HTTP 403 on a derivative

The itok is missing or wrong, usually because a proxy rewrote the query string or a cached page holds a URL from before a private key change.

The style form offers no AVIF option

GD was not built with libavif, or libgd is older than 2.3.2. Check gd_info() and use the fallback effect.

Large photos fail with a white page or a 500

PHP memory_limit is too low for the image; raise it, or reduce the original on upload with a Maximum image resolution limit on the field.

Derivatives look stale after changing a style

The style was imported but not flushed, or a CDN keeps the old file. Run image:flush and purge the CDN.

The hero image appears late

It is lazy. Set the hero’s formatter to eager.

Deleting an image style is refused

A display, responsive style or Media Library configuration depends on it. Replace the references first.

Checking the whole configuration

A short checklist for the Meridian site before a release.

# every display that uses an image style
ddev drush config:status
ddev drush php:eval 'foreach (\Drupal::entityTypeManager()->getStorage("entity_view_display")->loadMultiple() as $d) { foreach ($d->getComponents() as $n => $c) { if (!empty($c["settings"]["image_style"])) { echo $d->id(), " ", $n, " ", $c["settings"]["image_style"], PHP_EOL; } } }'

# regenerate the derivatives of the changed styles
ddev drush image:flush meridian_card_4x3
ddev drush cache:rebuild

Source: Drush: php:eval