Images and media
Editors upload one original image; the theme decides every size and crop that visitors actually download. This page covers the pipeline that makes that work: image styles, responsive image styles bound to your theme’s breakpoints, and the templates behind media entities.
Define the derivative sizes your design needs, serve the right one per viewport through a picture element, and override the markup around images and media. Authoring image-processing plugins (toolkits and effects) is out of scope here; that API is documented in the Image toolkits topic.
Prerequisites
Section titled “Prerequisites”- A Drupal 11 site with the Image module (standard installs have it). The responsive image section needs the Responsive Image module:
ddev drush pm:install responsive_image. - A theme with breakpoints defined in
THEME.breakpoints.yml; the first theme tutorial part 3 sets these up. - For the media section, the Media module with at least one media type.
Create an image style
Section titled “Create an image style”An image style is a named recipe of effects (scale, crop, convert) applied to an original to produce a derivative. Create one at Administer → Configuration → Media → Image styles/admin/config/media/image-styles: select Add image style, name it, and add effects. For a card thumbnail, one Scale and crop effect with the target dimensions is typical.
Image styles are configuration entities, so the result of clicking through the UI is a config export you can review and deploy:
# An image style is a config entity. This is what an export looks like# (drush config:export writes one such file per style).langcode: enstatus: truedependencies: { }name: theming_howtos_cardlabel: 'Card (480×270)'effects: # Effects are plugins; each instance is keyed by a UUID. b9bf2bcb-9a96-4a9b-8911-3aaa5e9b9c43: uuid: b9bf2bcb-9a96-4a9b-8911-3aaa5e9b9c43 id: image_scale_and_crop weight: 0 data: anchor: center-center width: 480 height: 270Derivatives are generated lazily. Saving the style creates no files; the first request for a derivative URL generates the file under sites/default/files/styles/STYLE_NAME/ and subsequent requests are served statically. The URL carries an itok token so visitors cannot fill your disk by requesting arbitrary derivative sizes. Editing or deleting a style flushes its existing derivatives.
Render derivatives
Section titled “Render derivatives”For a field, no code is needed: under Manage display, set the image field’s format to Image and pick the image style per view mode (a named rendering configuration such as teaser or full).
In code, build an image_style render array. A common place is a preprocess hook that hands the template a ready-to-print variable:
/** * Implements hook_preprocess_HOOK() for node templates. * * Exposes image derivatives to Twig as ready-to-print render arrays. * The derivative file itself is only generated on the first request * for its URL, not here. */#[Hook('preprocess_node')]public function preprocessNode(array &$variables): void { $node = $variables['node']; if (!$node->hasField('field_image') || $node->get('field_image')->isEmpty()) { return; } $uri = $node->get('field_image')->entity->getFileUri();
// One fixed-size derivative: {{ card_image }} in node.html.twig. $variables['card_image'] = [ '#theme' => 'image_style', '#style_name' => 'theming_howtos_card', '#uri' => $uri, '#alt' => $node->get('field_image')->alt, ];
// A <picture> element that picks a derivative per theme breakpoint: // {{ hero_image }}. $variables['hero_image'] = [ '#theme' => 'responsive_image', '#responsive_image_style_id' => 'theming_howtos_hero', '#uri' => $uri, '#attributes' => ['alt' => $node->get('field_image')->alt], ];}Then in node.html.twig: {{ card_image }}. The render element computes the derivative’s dimensions without generating the file, so the img tag gets correct width and height attributes and the page does not shift while images load.
Map breakpoints to image styles
Section titled “Map breakpoints to image styles”One derivative size is still wrong for someone: a 1200 pixel hero is wasteful on a phone, and a 480 pixel thumbnail is blurry on a wide screen. The Responsive Image module generates picture elements that let the browser pick per viewport, driven by two pieces:
-
Breakpoints come from your theme’s
THEME.breakpoints.yml; the first theme defines three, each available at 1x and 2x:web/themes/custom/first_theme/first_theme.breakpoints.yml # Breakpoints let Drupal's responsive image styles and other modules react# to the same screen sizes your CSS uses. Mobile first: smallest has no# media query.first_theme.mobile:label: MobilemediaQuery: ''weight: 0multipliers:- 1x- 2xfirst_theme.tablet:label: TabletmediaQuery: 'all and (min-width: 48rem)'weight: 1multipliers:- 1x- 2xfirst_theme.desktop:label: DesktopmediaQuery: 'all and (min-width: 75rem)'weight: 2multipliers:- 1x- 2x -
A responsive image style maps each breakpoint and multiplier to an image style. Build it at Administer → Configuration → Media → Responsive image styles
/admin/config/media/responsive-image-style: pick your theme’s breakpoint group, assign an image style per breakpoint, and set the fallback style used when nothing matches. Exported, it looks like this:web/modules/custom/docs_snippets/config/optional/responsive_image.styles.theming_howtos_hero.yml # A responsive image style maps each theme breakpoint (and multiplier) to# an image style. Build it at /admin/config/media/responsive-image-style;# this is the exported result.langcode: enstatus: truedependencies:config:- image.style.large- image.style.medium- image.style.widetheme:# The breakpoint group comes from first_theme.breakpoints.yml.- first_themeid: theming_howtos_herolabel: Heroimage_style_mappings:-image_mapping_type: image_styleimage_mapping: mediumbreakpoint_id: first_theme.mobilemultiplier: 1x-image_mapping_type: image_styleimage_mapping: largebreakpoint_id: first_theme.mobilemultiplier: 2x-image_mapping_type: image_styleimage_mapping: largebreakpoint_id: first_theme.tabletmultiplier: 1x-image_mapping_type: image_styleimage_mapping: widebreakpoint_id: first_theme.desktopmultiplier: 1xbreakpoint_group: first_themefallback_image_style: medium
Apply it the same two ways as a plain style: the Responsive image field format under Manage display, or a responsive_image render array (the hero_image variable in the preprocess snippet above).
For fluid-width images, the alternative sizes mapping type puts multiple candidate styles in one srcset with a sizes attribute and lets the browser do all the math; the Responsive Image module ships a single viewport_sizing breakpoint group for exactly that setup.
Theme media entities
Section titled “Theme media entities”Media items are content entities: they render through view modes, and each media type’s Manage display decides which fields show. The themable layers, from outside in:
media.html.twigwraps every media item. The Media module provides suggestions per view mode, bundle, and source plugin, somedia--image.html.twigormedia--remote-video--card.html.twigtarget one type or one type-and-view-mode pair, exactly like node templates (Twig in Drupal covers the workflow).- Inside it,
{{ content }}renders the configured fields; for an image media type that is an image field, so your image style and responsive image formatters from the previous steps apply here too. - From a host entity’s template, a media reference field renders with
{{ content.field_media }}like any other field, and the media item’s own templates take over inside it.
The practical recipe for “make embedded images look right site-wide”: create a dedicated view mode for the media type, configure its image field with a responsive image style, and select that view mode wherever the media is referenced (including the media embed dialog in CKEditor).
Verify
Section titled “Verify”- After defining the config above (or clicking it together in the UI), run
ddev drush cache:rebuild(ddev drush cr) and visit an article that has an image. - The page source shows a
pictureelement for the hero with onesourceper breakpoint, and theimgfallback pointing at a/sites/default/files/styles/...URL with anitokquery parameter. - The derivative files exist on disk under
sites/default/files/styles/only after the page has been requested once. - Resize the browser window across your breakpoint widths (48rem and 75rem in the first theme) and confirm the loaded image swaps in the network panel.
Variations
Section titled “Variations”Flush derivatives after changing effects
Section titled “Flush derivatives after changing effects”Derivatives are cached files; if you change a style outside the UI (config import, update hook), flush its old files with ddev drush image:flush STYLE_NAME (or all styles with ddev drush image:flush --all).
WebP and AVIF derivatives
Section titled “WebP and AVIF derivatives”Conversion is an effect like any other: add a Convert effect to the style. Core’s own large style converts derivatives this way, so the source file format stops mattering for delivery weight.
Art direction versus resolution switching
Section titled “Art direction versus resolution switching”Mapping different crops per breakpoint (art direction) and different sizes of the same crop (resolution switching) both work with the per-breakpoint mapping; pure resolution switching is often served better by one sizes mapping. If your image styles only scale, start with sizes.
Related
Section titled “Related”- Build your first theme 6: settings, responsive images, and shipping applies this pipeline inside the tutorial theme.
- Twig in Drupal for overriding
media.html.twigand friends. - Configuration management workflow for deploying the exported styles.
The image.factory serviceandthe Image toolkits topicare the entry points if you need to process images in PHP yourself.
Was this page helpful?
Drupal is a registered trademark of Dries Buytaert.