Breakpoints and Responsive Images

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.

A phone, a laptop and a gallery wall display need different pixels from the same photograph. Drupal solves this in two steps. A theme (or a module) declares breakpoints in a YAML file, and the core Responsive Image module maps those breakpoints to image styles and renders markup that lets the browser pick the right file. This page was checked against the official documentation on 2026-10-30 and is written for Drupal 11.4.x, PHP 8.3 or later and Drush 13 or 14. Drupal 10 ends on 2026-12-09 and Drupal 12.0.0 is planned for the week of 2026-12-07, so new sites should start on Drupal 11. Everything here applies to both lines unless a version is named.

The examples belong to the Meridian Museum scenario: the theme meridian shows a wide hero photograph on each exhibition and a grid of square object photographs for the collection (meridian_collection). The image styles that the responsive styles map to are created as described in Display modes and image styles, which also covers lazy loading of the plain Image formatter and format conversion; this page does not repeat that material.

The pieces and who uses them

Piece What it does Who works with it

THEME.breakpoints.yml

Declares named media queries, their order and the pixel-density multipliers they support. There is no screen for it.

Themer

Breakpoint module (core)

Reads those files from all enabled themes and modules and exposes them as breakpoint groups.

Developer (API), invisible to editors

Responsive Image module (core)

Provides the responsive image style configuration entity, its administration screen and the Responsive image field formatter.

Site builder

Image styles (core Image module)

Produce the actual resized files (the derivatives) that the mapping refers to.

Site builder

Field formatter or Twig/render array

Chooses which responsive style renders an image in a view mode, a view or custom markup.

Site builder, themer

The Responsive Image module depends on the Breakpoint and Image modules, so enabling it enables them. Enable it with drush pm:install responsive_image (or drush en responsive_image) and export the result with the rest of the configuration, see Configuration management.

flowchart LR BY[meridian.breakpoints.yml] --> BG[Breakpoint group meridian] BG --> RS[Responsive image style meridian_hero] IS[Image styles meridian_hero_sm, md, lg] --> RS RS --> F[Responsive image formatter] F --> M{Mapping type} M -- sizes --> IMG[img with srcset and sizes] M -- single style per breakpoint --> PIC[picture with one source per breakpoint] IMG --> B[Browser picks one file] PIC --> B

Declaring breakpoints: THEME.breakpoints.yml

A theme or module that wants its breakpoints known to Drupal places a file named after itself in its root directory: meridian.breakpoints.yml in the theme meridian, or meridian_collection.breakpoints.yml in a module. The official documentation states that Drupal offers no user interface for editing breakpoints; the file is the only place. A new file is only discovered after a cache rebuild (drush cache:rebuild).

Each entry is keyed by a machine identifier and has these keys:

Key Meaning

label

Human-readable name. It is shown in the responsive image style form.

mediaQuery

The media query text. An empty string means "no query" and is typical for the smallest, mobile-first breakpoint.

weight

The order. Breakpoints with smaller minimum widths get smaller weights. Modules usually sort from the smallest to the largest, though a consumer can reverse this, and the Responsive Image module orders its mappings with the breakpoint weight and then the multiplier.

multipliers

The device-pixel-ratio multipliers the breakpoint supports. The Breakpoint module itself defines 1x, 1.5x and 2x; list only those you want editors to be able to map.

group

Optional. When omitted the group is the name of the theme or module that declares the file.

# web/themes/custom/meridian/meridian.breakpoints.yml
meridian.mobile:
  label: Mobile
  mediaQuery: ''
  weight: 0
  multipliers:
    - 1x
    - 2x
meridian.tablet:
  label: Tablet
  mediaQuery: 'all and (min-width: 48em)'
  weight: 1
  multipliers:
    - 1x
    - 2x
meridian.desktop:
  label: Desktop
  mediaQuery: 'all and (min-width: 75em)'
  weight: 2
  multipliers:
    - 1x
    - 2x

Identifiers and groups

A breakpoint identifier is the theme or module name, a dot and the breakpoint name (meridian.tablet). All breakpoints declared by one file share one group named after the theme or module, and the group is what the responsive image style form asks you to choose. To place a breakpoint in a group with a different name, give the entry a group key and use the group name in the identifier, in the form provider.group.name. A module can add a breakpoint to a group that a theme or another module defined by using that group’s full name in the identifier and in the group key. The documentation says custom group labels are not supported, so the machine name of the group is what appears in the screens.

# web/modules/custom/meridian_collection/meridian_collection.breakpoints.yml
# Adds a wide breakpoint to the group defined by the meridian theme.
meridian.wall:
  label: Gallery wall
  mediaQuery: 'all and (min-width: 120em)'
  weight: 3
  group: meridian
  multipliers:
    - 1x

Treat this second form with care. It couples a module to a theme, and if the theme is not installed the group simply does not exist. Most sites need only the simple file in the theme.

Writing good media queries

  • Use a mobile-first scheme: an empty query for the smallest breakpoint and min-width queries after it. The mapping screen then reads in the same order as the cascade of the stylesheet.

  • Prefer em over px in the query so that the breakpoint follows a user’s font size; the example uses 48em and 75em.

  • Keep the file and the CSS in sync. A CSS custom property cannot be used inside a media query, so the same numbers live in two places: the YAML file and the stylesheet (or the Sass or PostCSS configuration that generates it, see Front-end tooling). A breakpoint that exists only in CSS is perfectly valid; put a breakpoint in the YAML file only when Drupal needs to know about it, for example for responsive images.

  • The weight values must follow the order of the minimum widths. A wrong weight produces a mapping table whose rows are in the wrong order and a picture element whose source elements the browser evaluates in the wrong order.

Multipliers

A multiplier describes a device pixel ratio. The 2x entry of the tablet breakpoint means "the file for a tablet whose pixels are twice as dense". Each breakpoint and multiplier pair is a separate row on the responsive image style form, so listing 1x and 2x for three breakpoints produces six rows. List only the multipliers you will map, as every row is a decision and an image style to maintain. Unmapped multipliers cause no error; the browser falls back to the files it has.

Responsive image styles

A responsive image style is a configuration entity that holds a breakpoint group, a fallback image style and a list of mappings from breakpoint and multiplier pairs to image styles. It is managed at Configuration > Media > Responsive image styles (/admin/config/media/responsive-image-style), which requires the permission Administer responsive image styles. The User Guide describes it as a mapping between image styles and breakpoints that can use the breakpoints of the theme.

The form has these parts:

  1. Label and machine name, for example meridian_hero.

  2. Breakpoint group: choose meridian. Changing the group of an existing style clears every mapping, because the old rows would refer to breakpoints that are no longer in the group.

  3. Fallback image style: the style used for the img element when the browser does not support srcset or picture. Pick a mid-sized one, such as the style for the tablet. It is also the file that crawlers and legacy clients receive.

  4. For each breakpoint and multiplier row, a choice of what to output (see below).

Mapping types

Each row is one of these:

  • Select multiple image styles and use the sizes attribute: you give a sizes value and tick several image styles. The output is an img (or a source) with a srcset of width descriptors (640w) and a sizes attribute. The browser chooses the file.

  • Select a single image style: one style for that breakpoint and multiplier. The output is a picture element with a source carrying the breakpoint’s media query and, for the multipliers, a srcset with 1x and 2x descriptors.

  • The empty image option: the identifier _empty image_. Below or at this breakpoint nothing loads. It is useful for decorative images that should disappear on small screens.

  • The original image option (_original image_): no derivative, the uploaded file is served. It skips every effect, so large uploads are served in full size; avoid it for editor-uploaded photographs.

The two constants EMPTY_IMAGE ('_empty image_') and ORIGINAL_IMAGE ('_original image_') are defined by ResponsiveImageStyleInterface, and a mapping counts as empty unless it has an image style (single-style type), or both a sizes value and at least one style (sizes type).

The stored configuration

The entity is stored as responsive_image.styles.<id>. Each mapping has a breakpoint_id, a multiplier, an image_mapping_type (image_style or sizes) and an image_mapping: a style identifier for image_style, or an array with sizes and sizes_image_styles for sizes. The fallback and the group are fallback_image_style and breakpoint_group.

# config/sync/responsive_image.styles.meridian_hero.yml
langcode: en
status: true
dependencies:
  config:
    - image.style.meridian_hero_lg
    - image.style.meridian_hero_md
    - image.style.meridian_hero_sm
  theme:
    - meridian
id: meridian_hero
label: 'Meridian hero'
image_style_mappings:
  -
    breakpoint_id: meridian.mobile
    multiplier: 1x
    image_mapping_type: sizes
    image_mapping:
      sizes: '100vw'
      sizes_image_styles:
        - meridian_hero_sm
        - meridian_hero_md
  -
    breakpoint_id: meridian.tablet
    multiplier: 1x
    image_mapping_type: sizes
    image_mapping:
      sizes: '100vw'
      sizes_image_styles:
        - meridian_hero_md
        - meridian_hero_lg
  -
    breakpoint_id: meridian.desktop
    multiplier: 1x
    image_mapping_type: image_style
    image_mapping: meridian_hero_lg
breakpoint_group: meridian
fallback_image_style: meridian_hero_md

Because the entity computes its dependencies from the breakpoint group’s provider, the fallback style and every style named in a mapping, exporting it brings the theme and the image styles along. The consequence is the usual rule of configuration management: import the image styles and the responsive style together, and do not delete an image style that a responsive style uses. Drupal refuses to delete a style that is in use.

Check what is stored:

ddev drush config:get responsive_image.styles.meridian_hero
ddev drush config:export --yes

srcset and sizes versus picture

The two outputs solve different problems and the choice is made per row, not per site.

Resolution switching with srcset and sizes

When the question is "the same crop at several sizes", use the sizes mapping type. The markup tells the browser the widths of the available files (640w) and how wide the image will be in the layout (sizes), and the browser combines that with the screen size and pixel density to pick one file. You do not need one row per device. A typical result for the hero (the layout is full width up to the maximum of the page) looks like this:

<img loading="lazy"
     srcset="/sites/default/files/styles/meridian_hero_sm/public/hero.jpg?itok=AAAA 640w,
             /sites/default/files/styles/meridian_hero_md/public/hero.jpg?itok=BBBB 1024w"
     sizes="100vw"
     src="/sites/default/files/styles/meridian_hero_md/public/hero.jpg?itok=BBBB"
     width="1024" height="576" alt="A dome seen at dusk">

The sizes value is a string of the browser’s own syntax: a media condition and a length, repeated, ending with a default length, for example (min-width: 75em) 1200px, 100vw. Drupal does not validate it. A wrong sizes is the most common reason for oversized downloads: a sizes of 100vw for an image that fills only a third of the page makes the browser fetch a file three times larger than needed. Measure the rendered width in the layout and write that.

Art direction with picture

When the question is "a different crop at different sizes" (the wide panoramic hero on desktop, a tight crop of the dome on a phone), use the single image style type for each breakpoint. Each style then differs in its Scale and Crop, Crop or Focal Point effects, not only in size. The output is a picture element with one source per breakpoint and media query, and the browser uses the first matching source.

<picture>
  <source srcset="/sites/default/files/styles/meridian_hero_lg/public/hero.jpg?itok=CCCC"
          media="all and (min-width: 75em)" type="image/jpeg" width="1600" height="600">
  <source srcset="/sites/default/files/styles/meridian_hero_md/public/hero.jpg?itok=BBBB 1x,
                  /sites/default/files/styles/meridian_hero_md2x/public/hero.jpg?itok=DDDD 2x"
          media="all and (min-width: 48em)" type="image/jpeg" width="1024" height="400">
  <img loading="lazy" src="/sites/default/files/styles/meridian_hero_sm/public/hero.jpg?itok=EEEE"
       width="640" height="640" alt="A dome seen at dusk">
</picture>

Three details come from the module’s own code. If every file in a srcset shares one MIME type, the source gets a type attribute, which lets the browser skip a format it does not support; if one breakpoint lists several multipliers they are expressed as 1x, 2x descriptors; and the width and height are passed through when known so the browser can reserve space. Width descriptors and multipliers cannot be mixed in a single srcset (the HTML specification forbids it), so when a breakpoint has several multipliers in the sizes type the module merges them into width descriptors and drops the multipliers.

Choosing

Need Use Why

The same picture at different resolutions (collection thumbnails, news images)

sizes mapping, a few widths

One row per breakpoint is enough, and the browser accounts for the pixel density.

A different crop or aspect ratio per screen (exhibition hero)

Single image style per breakpoint (picture)

Only picture can change what is shown, not just how big.

Different file formats with a fallback

Single image style rows whose styles end in different Convert effects (WebP, AVIF, JPEG)

The type attribute of each source lets the browser pick a format it can decode. The Convert effect itself is described in Display modes and image styles.

An image that is decoration on a phone

_empty image_ for the small breakpoint

Nothing is downloaded there.

Both can be combined in one style: the sizes type for some rows and the single-style type for others.

Using a responsive style

In a view mode

Select the Responsive image formatter for an image field on the Manage display tab of the content type, pick the responsive style and the link and loading settings. The formatter plugin has the identifier responsive_image, applies to image fields only and its settings are responsive_image_style, image_link and image_loading.

# 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
    third_party_settings: {  }

The formatter adds the responsive style and every image style that it references to the cache tags of the rendered element. Editing an image style or the mapping therefore refreshes the pages that show it, and Performance and caching explains how those tags are invalidated. Media items, the Media Library and the image fields of Views use the same formatter (see Media and the Media Library and Views).

In a render array

A custom block or controller can render an image through the same theme hook that the formatter uses.

<?php

declare(strict_types=1);

namespace Drupal\meridian_collection\Plugin\Block;

use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Shows the featured object photograph with a responsive style.
 */
#[Block(
  id: 'meridian_featured_object',
  admin_label: new TranslatableMarkup('Featured object'),
)]
final class FeaturedObjectBlock extends BlockBase {

  public function build(): array {
    return [
      '#theme' => 'responsive_image',
      '#responsive_image_style_id' => 'meridian_hero',
      '#uri' => 'public://collection/orrery.jpg',
      '#width' => 1600,
      '#height' => 900,
      '#attributes' => [
        'alt' => $this->t('A brass orrery from 1790'),
        'loading' => 'lazy',
      ],
    ];
  }

}

The template responsive-image.html.twig renders this hook, and the formatter uses responsive-image-formatter.html.twig around it, so both can be overridden in the theme with the suggestions described in Template suggestions and preprocess. Pass the dimensions when you know them; the module treats them as optional and passes them on to the markup.

Version note on preprocess functions

The procedural template_preprocess_responsive_image() and template_preprocess_responsive_image_formatter() were deprecated in Drupal 11.3.0 and are removed in 12.0.0. The same applies to the helper functions responsive_image_get_image_dimensions(), responsive_image_get_mime_type() and _responsive_image_build_source_attributes(): the code moved to the ResponsiveImageBuilder service and to ResponsiveImageThemeHooks. Custom code that called these functions directly must use the service. Preprocess functions in a theme (meridian_preprocess_responsive_image()) are unaffected.

Lazy loading

The image_loading setting offers lazy and eager; the default is lazy, and the chosen value is written to the loading attribute of the rendered img. The rule is the same as for the plain Image formatter:

  • Set the one image that is the largest element in the first viewport (the Meridian hero) to eager. A lazy hero delays the largest contentful paint.

  • Leave all others on lazy, particularly the grid of collection thumbnails, so offscreen files are not downloaded.

  • Lazy loading is a hint and does not change the file size; correct sizes values do.

  • Keep width and height on the element or reserve the space in CSS with aspect-ratio, otherwise lazy images shift the layout when they arrive. With the picture type, the aspect ratio can differ per source, so the dimensions that the module outputs on each source matter.

A theme that overrides the template or the preprocess function to force loading per style is possible, and old tutorials and issue discussions do it, but since the formatter exposes the setting, change the display configuration first. The change records for the setting (node 3173719 and node 3272098) describe it as configurable in the image formatters. Accessibility and performance together are discussed in Accessibility and performance in themes.

Debugging a responsive image

  1. Rebuild caches after adding or changing meridian.breakpoints.yml, then open the responsive style form. If the group is missing, the file name or its YAML is wrong.

  2. Turn on Twig debugging (see Twig templates and debugging) and check that the responsive-image.html.twig template is used and that picture or srcset appears in the markup.

  3. Open the browser developer tools, resize the window and watch the network panel: the chosen file changes at the breakpoints, and the "current source" shown by the inspector tells which candidate the browser selected.

  4. Check ddev drush config:get responsive_image.styles.meridian_hero against the form.

  5. Look for the generated files under sites/default/files/styles/; the first request for a derivative creates it, as described in File system and private files.

Security notes

  • Derivative URLs carry an itok token tied to the image style, so a visitor cannot request arbitrary sizes. This is also why a responsive style with many styles and multipliers multiplies the number of derivatives created on a first request. A cache in front of the site, a warm-up after publication and a limit on the number of styles keep that cost down.

  • Images of private files are served through Drupal, and a responsive style does not change access checks; see File system and private files.

  • Alt text is not a part of the responsive style. An empty alt marks the image as decorative; set it on the field, as covered in Media and the Media Library.

  • The _original image_ option serves the editor’s upload as is, including its size and embedded metadata. Do not use it for uploaded photographs.

Common errors and pitfalls

Symptom Cause and remedy

The responsive style form shows no breakpoint group

The theme is not enabled, the file name does not match the theme machine name, or the YAML is invalid. Caches need a rebuild.

Mapping rows disappeared

The group of the style was changed; Drupal clears all mappings when the group changes.

The wrong image is chosen on a wide screen

The weight values do not follow the order of the minimum widths, or a max-width query overlaps a min-width query.

The phone downloads the desktop file

The sizes value claims a larger width than the layout gives (100vw in a three-column grid), or only the fallback is mapped.

Blurry images on high-density screens

The 2x multiplier is not listed in the breakpoint or has no image style.

A layout jump when images load

No width and height, or a picture whose source crops have different ratios and no aspect-ratio rule.

The hero loads late

It is left on lazy. Set the formatter’s loading to eager for that one image.

The style cannot be deleted

It is used by a display. Change the displays first.

An image in the body text ignores the responsive style

Images typed into formatted text are plain img elements. Use media embedded in the text (see Text formats and CKEditor 5) with a view mode that selects the formatter.

Older books and tutorials

Modernizing Drupal 10 Theme Development (2023) devotes a chapter to responsive images and is the best structured treatment in the books: the breakpoint file, the Responsive Image module and the mapping screen are all described. The module and the YAML format have not changed, so the concepts remain valid. Check four things against current practice:

  • It was written for Drupal 10.1, before the PHP-attribute formatters (10.2) and the deprecation of the procedural responsive image preprocess functions in 11.3; code that implements or calls them must use the services shown above.

  • Tutorials of the Drupal 8 era keep loading handling in preprocess functions or contributed modules (such as the Lazy module with its own responsive formatter). Core has offered lazy and eager loading in the formatters for years, so remove such workarounds.

  • Format advice written then lists JPEG, PNG and WebP only. The Convert effect also supports AVIF in recent releases, as described in Display modes and image styles.

  • Olivero and Claro are removed in Drupal 12, so a book example that copies a core theme’s breakpoints is best replaced by the theme’s own file in a Starterkit-generated theme (see Starterkit and subthemes).

Drupal 10 Masterclass and Drupal 10 Development Cookbook (both 2023) were written for Drupal 10.0 and 10.1; where they describe the administration screens for images, check the details against the current release. See Where older books and tutorials are outdated for the general list.