Problem/Motivation
Images in pages created with Experience Builder are being shown in their uploaded format i.e. not optimised for size. Developers of components should also have access to an easy method to optimise images e.g. a Card component includes an image and the component author should be able to define what size the image should be when displayed inside the component.
The existing Responsive Images core module doesn't quite meet these requirements as it requires quite a bit of complex configuration involving setting global breakpoints, image styles, and mappings. This also introduces a dependency on configuration, which is not currently supported by SDC.
We would also like to support a client-side component similar to (or even re-using) https://nextjs.org/docs/pages/api-reference/components/image
Proposed resolution
Take inspiration from Revive this module which was built for Contenta: https://www.drupal.org/project/image_style_dynamic
It provides a method to access the image effects used by image styles through a URL, for example:
/sites/default/files/styles/image_scale_and_crop[width]=640&image_scale_and_crop[height]=300&image_convert[extension]=webp/public/2025-04/image.jpg
⚠️ This aims to solve only image resizing, not the full gamut of possible image optimizations/transformations. Only <img srcset>, using and using only a width descriptor, not a pixel density descriptor.
The default image SDC should be updated to automatically handle the following:
- Automatic responsive image handling - Generate appropriate srcsets for different device sizes, by:
- … passing a per-component instance-prop-shape URI template to the component instance (such as
/sites/default/files/styles/xb--{width}/public/2025-06/foo.jpg.webp?itok=1Rl59WAb) - globally defining supported widths (in simple config) to allow the code generating a
srcsetto generate multiple concrete URLs from that template - Thus allowing essentially
$candidate_strings = []; foreach ([640, 750, 828, 1080, 1200, 1920, 2048, 3840] as $width) { $candidate_strings[] = str_replace('{width}', $width, $uri_template) . ' ' . $width . 'w'; } $srcset = implode(', ', $candidate_strings); - making
itokthe same for all different widths … but also allowing ONLY those globally defined widths, thus protecting against DDoS
- … passing a per-component instance-prop-shape URI template to the component instance (such as
Descoped
- Modern image formats - Support WebP and AVIF with fallbacks for older browsers
- Loading optimizations - Implement lazy loading with blur placeholders
- Layout stability - Prevent content shifting during page load
- Quality optimization - Balance quality and performance
- Priority loading - Allow critical images to load immediately
- Focal point - Support for contrib modules like Focal Point
- CDN support - Support for CDN-generated images
Remaining tasks
- Review and fix garbage collection. Generated derivatives should be cleared when an image is deleted, as well as flushed on demand
- Ensure that the module can function with security tokens (i.e.
itok) - Come up with a plan for how the client-side component could make use of this securely i.e. without opening up the site to DDOS attacks through mass image generation. Next.js does this by limiting image generation to a pre-configured set of device widths.
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | test7.png | 844.48 KB | neha_bawankar |
| #64 | test6.png | 1.51 MB | neha_bawankar |
| #45 | Screenshot 2025-06-24 at 8.58.11 PM.png | 331.44 KB | wim leers |
Issue fork experience_builder-3515646
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #5
nagwani commentedComment #6
effulgentsia commentedChanging tag from "sprint" to "spike", because we're not planning on completing this issue this sprint, we're just trying to get a handle on what Claude Code prompted by @grasmash created here.
Comment #7
catchWhy is this re-implementing responsive images and image styles almost from scratch?
edit: it would have been nice if the issue summary had clearly stated this was an AI-generated slop experiment so I could have ignored the MR before trying to review it.
Comment #8
catchThis desperately needs an issue summary update with the following:
What is the requirement for XB?
How far does core's existing image style and responsive image support go towards meeting the requirement?
If there are gaps, what are the specific gaps and do they already have existing issues open to resolve? And is it technical capabiities, usability etc.?
Right now there is only a solution without a problem statement.
Comment #9
lauriiiComment #10
catchResponding to the new issue summary (thanks for writing it up):
The SDC principle is that it should receive the props, but the responsive image style config itself doesn't need to be a prop - instead the SDC component could receive the image URLs + dimensions + alt text, which can be built in advance based on the data stored in Experience Builder itself and passed in - just enough information to generate the img + srcset (or picture element) from.
Since the URL or URLs contain all the necessary information for the image style callback to render the image, the SDC doesn't need to know anything about anything else - it can render the markup/attributes based on what's passed in.
At the moment, any component library would have to re-implement all the various configuration options in the SDC added here in order to support responsive images, whereas really they should just be able to take the minimum set of information to build the markup.
Unless the only way to render a responsive image in XB is going to be nesting this component in a slot? Also what happens if a component wants to accept either a responsive image or an external one (which iirc is a requirement)?
The current implementation is shoe-horning half a dozen configuration options into the component, including whether AVIF or webp should be used. In general, avif or webp feels much more like a site-building task than a content editor one - why add the load of choosing this every time? There could eventually be even more formats available, as well as composite formats, like avif with webp fallback, potentially even with a file size check if we do #3516434: Consider falling back to webp from AVIF based on filesize. Is every combination of those going to have to be provided in the SDC as they're added? This really doesn't seem to be something to configure for every XB component.
On top of this, the existing image style system provides a lot more options than are mentioned here: cropping which is necessary when uploads can be different aspect ratios, focal point crop etc. I imagine Experience Builder would also want to support these - are they going to need to be baked into the SDC component too?
The responsive image module in Drupal Core is very flexible and from that perspective works well. However, it is very hard and tedious to use.This is a reason to improve the usability of responsive images in core, not to build an entirely parallel system that may or may not be able to do the same things and can only be used by experience builder.
Comment #11
lauriiiBut where would you choose the responsive image style in this scenario? This should not be up to the content creator to decide. The component should always render with the same (responsive) image style.
I looked at
\Drupal\image\Controller\ImageStyleDownloadControlleragain and it looks like we may be able to re-use the download parts of image styles. I think this is something that @justafish indicated she would be looking into. There's already a PoC in https://git.drupalcode.org/project/image_style_dynamic/-/tree/8.x-1.x?re... for this.I somehow had the impression that the garbage collection of images was relying on file entities but after reviewing this again, it doesn't seem to be the case. Based on this, we should be able to use some of the lower level APIs from image styles. I removed this challenge from the issue summary.
Based on above, we may be able to provide additional components for loading image styles and responsive images from components as a fallback.
The component is not necessarily designed to be exposed in XB – it's a developer tool for whoever is building the components. Also, there are settings that should not be in the component itself. For example, the image types that are generated should be a global configuration. See https://nextjs.org/docs/pages/api-reference/components/image#advanced for how Next.js does this.
We should support modules like focal point with the component. I'm not sure if that's the case for the MR as it currently stands but it is mentioned in the issue summary.
It would be very hard to iteratively address this in the responsive image module. I think it would be better to build an alternative module in contrib, and battle test it before putting it to core.
Comment #12
catchThis seems like something for a site builder to configure. Even if not using responsive image styles as such, there needs to be settings for things like whether to crop to specific aspect ratios etc. as discussed above, which should never be exposed to content creators on a per-image basis either. So there has to be some kind of configuration step for the component before it's available to site builders, not just within the component at the content creator level, and then it can happen at that step.
Yes it has nothing to do with file entities, this is why it's good to verify requirements before thousands of lines of code are written, even if an LLM is writing the code.
If the image type is a global setting, then why is the MR passing it to the component as a prop in the first place? And why is the component controlling which types are available? I think the answer to this is 'because the MR was written by Claude and posted without human review without clear instructions' but that's why I asked for an issue summary here defining the actual requirements.
Also if for some reason the format (or anything else) is taken from global configuration, then translated to a string to pass to the component, how is this translation layer different to what would be necessary to use a responsive image style to generate the relevant attributes and URL from to pass to the component?
Whether the work was done directly in core or in contrib, it would still be a coherent module in its own right, and not an entire subsystem hidden in experience builder.
Comment #13
catchOK but from that page,
this is not very encouraging.
Comment #14
effulgentsia commentedAdding important info to the issue title per #7.
Comment #15
lauriiiThe proposal is for the "(responsive) image style" to be able to live inside component code.
For example, the card component may do the following:
This would automatically generate the image with the width 250px.
The hero component might do the following:
This would automatically generate a responsive image style where the highest width would be 2500px.
In future, we could add support to configure these using a config entity so that they could be used in the UI.
Looking at this again, it does look like the garbage collection only works for file entities because the only place where
\image_path_flushis called is\Drupal\image\Hook\ImageHooks::filePredeleteand\Drupal\image\Hook\ImageHooks::fileMove. So if we use image styles for non-file entities, we'd have to add another way of handling garbage collection.There's nothing wrong in having global settings in configuration so long as the set of configurations is predictable, so that we are not introducing a dependency from components to a specific config entity which may or may not exist on a site. We can also provide sensible defaults for the configured image types. The problem with image styles and responsive images is dependency on specific config entities that may or may not exist, and the fact that they are extremely difficult to configure. Even if they exist, they may or may not have the configuration expected by a specific component because how would a component know if e.g., "small" image style has the configuration that the component expects?
This is one of the problems solved in the current MR. I think it would be better to wait for an actual proposal until doing further evaluation on this level.
Comment #16
catchImage styles as a whole can be flushed, which will clean anything remaining out, however also any new system would also have to add garbage collection both for styles and individual images.
If something is using image styles with files that aren't managed files, then it would have its own responsibilities to track usage/deletion etc. and respond to those.
But... I don't see how the equivalent of an image style flush can be achieved with the current proposal, and neither does it handle deletion of individual files.
If a component defines a 2500px responsive image style which generates say five different image derivatives, then the hmac in the MR will ensure that no-one can generate a 2499px responsive image style arbitrarily. However, if a site stops using that component (swaps it out for one that's 1800px instead), how will the system know that it needs to delete all of those images (or not if the component is used elsewhere in a different layout)? This could be hundreds of thousands of derivative images on disk to worry about.
Equally, separate 2000px and 1000px responsive image styles could end up both producing a 500px derivative, so to avoid over-purging, it would be necessary to determine not only when a responsive image style and derivative sizes are removed, but also the intersection of derivative sizes across the site. And garbage collection would still need to be implemented for when files are deleted (if there's a way to discover all possible derivative locations for an image, which it is not at all clear that there would be).
Equally, because the hmac only validates that the URL was generated from within Drupal, and doesn't rely on configuration, even if there is some kind of event that deletes images when the last usage of a component is removed, nothing stops cached HTML requests from regenerating those images again because the URL is always going to be valid. You might not be able to regenerate lots of URLs for a DDOS attack but they might be referenced from internet archive or similar (or still in a CDN). Both image styles and asset aggregates have explicit protection against this kind of stale file disk filling situation.
Comment #17
tonypaulbarkerHi folks. Here's an outline of my thoughts based on the discovery and feedback I have been involved with since last summer.
There are a lot of things on the wishlist in the description. Not all of these should be configurable per instance by editors. We will arrive at a difficult to use UX. They shouldn't be concerned with the filetype, lazy loading and so on. Most of these things should be in other settings away from the edit screen (or at least tucked away in 'advanced').
I am not concerning myself with how one would go about deleting files but - from the UX end in the editor - the important things for editors to be able to control in the content pane are:
Select size (max width or height) - grids and containers the image is positioned in are ideally known by the system from the layout to set an upper limit.
Select orientation from portrait, square or landscape (to limit the aspect ratio list)
Select aspect ratio
Crop (automatic with focal point, then tick a box to manually cropping controlled by the selected aspect ratio + allow freeform)
Brightness, contrast adjustments
Rotation / angle using a dial widget
Override alt text (once we have made adjustments like cropping, the description of our image changes from that stored in the library).
I don't think it's enough to only set these things for all instances of a component but defaults for the component are good. Administrators should be able to have some control to toggle these being available - in some cases, for example, the aspect ratio should be locked but cropping to that aspect ratio enabled. Editors really want to be able to make adjustments specific to their image for the context it is being used in without affecting other instances.
I don't believe that the 'legacy Drupal' image style system can accommodate this. But live adjustments could be generated with CSS to hand over to be generated with PHP based on the parameters on save.
Comment #18
justafishComment #19
justafishComment #22
catchAnother question about requirements - there's no mention of art direction here. Core responsive images use the
pictureelement, which makes it possible to use different aspect ratios at different breakpoints. This is part of what makes core responsive images complicated to set up (just using srcset would be a lot simpler and works for most cases, and it'd be good if the responsive images UI made that more of an obvious choice) - but, has there been an explicit decision not to supportpictureand art direction here? Or has it been deferred to a later phase? Or has it not been discussed yet?Comment #23
lauriii#22:
pictureis like another layer on top of the image component. See https://nextjs.org/docs/app/api-reference/components/image#getimageprops for how Next.js manages this and some other use cases with their API.Comment #24
tonypaulbarkerSome more ramblings for consideration.
We have precedent for sending parameters to image styles without building images with a URL, which is Crop API.
I'm thinking something along the lines of 'Adjustments API' similar to Crop API that could handle other types of adjustments like brightness, contrast, masks or a parameter for detecting subjects and cutting them out.
I came across Media Contextual Crop group of modules but I haven't taken them for a spin yet, https://www.drupal.org/project/media_contextual_crop . I think there is a distinction between content images where contextual information is available and more control is desirable and meta images that may be used for things like teasers and the context is not known by the content.
A reason to steer away from generating images with URLs is that even if width and height can be handled effectively we see from problems with facets https://www.drupal.org/forum/support/post-installation/2025-02-20/drupal... how the permutations multiply rapidly and it would leave us unable to extend the system.
Comment #25
fagoJust found stumbled over this (very interesting) issue.
We've been working on solving the same challenge for our projects at drunomics, where we use Vue/Nuxt on the frontend side. The solution we settled is having Drupal generate a high-res image in the right aspect-ratio using editorial controlled cropping, e.g. focal point + using nuxt-image for frontend-controlled, responsive image size generation.
That said, the approach taken by nuxt-image might be worth a look here: It features providers for the image resizing, i.e. it's doing the width/size calcuation based upon the given "sizes" attribute what is convenient for responsive images. Then, by having pluggable providers one can easily swap out a local/drupal-powered resizing resolution with a CDN-provided service.
Comment #26
effulgentsia commentedI like the main concept of this MR, which is to have "dynamic image styles", which I see as embodying three things:
itok, thereby allowing the front-end to pick the variation by just varying a part of the URL without needing to also worry about getting a differentitokfor each one. This broadening ofitokfrom giving access to a single derivative of a single image to giving access to a set of derivatives of a single image would make this easier to integrate into common front-end image components like the ones in Next or Nuxt. However, to prevent DDOS attacks, this means the set of variations within a group (e.g., the set of widths) needs to be reasonably bounded. This MR bounds to 8 hard-coded widths matching the defaultdeviceSizesin Next.js. We might want this to be configurable in some way, whether we add that configurability here or in a follow-up.With the above preamble out of the way, some thoughts about the details...
I agree, and I don't think we need the full genericism of being able to specify any combination of effects within the image style name. I think our use-case for now is just about widths. It's conceivable we'll want additional things to be dynamic in the future (format, quality, focal point, etc.), but I don't think we need to solve for that now. I think it's equally possible that those settings don't actually need to be dynamic in the same way that width does.
In other words, I think XB's use case could be addressed with short image style names like
xb--WIDTH(e.g.,xb--640) or whatever other delimiter we want to pick if we think there's something preferable over--.That is indeed one option, but I don't think it's optimal, because it would add clutter to image style listing and selection UIs, and image styles don't implement
lockedorno_ui, so we'd need to add code to prevent them from being edited or else let them be edited but figure out how to deal with them getting out of sync (e.g., their name not matching the width setting). Plus we'd still need to implementitoksharing, so they'd be in this in-between concept of sort of dynamic and sort of static. Overall, I think there's more downside than upside to have a stored config entity for each width.One option would be for XB to implement hook_cache_flush(), hook_file_move(), and hook_file_predelete(). However, that wouldn't exactly provide parity with regular image styles, because regular image styles are not flushed by a regular cache clear and can be flushed on their own via the UI or a Drush command. So if we want parity with that, I propose that we create an image style that represents the group. For example, we ship with an
xbimage style, which then exists as a stored config entity, thatxb--*image styles are "derived" from (but not individually stored). Then we can implementhook_image_style_flush()to flush all the--*ones whenever the base one is flushed.I don't like this being added to the json schema definition of the Image prop type. Prop types should be independent from Drupal's data model. What I recommend instead of this is adding a
srcsetTemplateproperty that's of type uri-template. So its value for an image foo.jpg would be/sites/default/files/styles/xb--{width}/public/2025-06/foo.jpg.webp?itok=.... Client code could then generate asrcssetfrom this by replacing{width}. Or, if integrating with a Next or Nuxt image component, you'd configure a loader/provider function that does that replacement.Per the top of this comment, we need the same itok for each width. This MR already implements a param converter that instantiates the image style even though it's a dynamic image style that doesn't exist in config storage. I think the only addition needed for this is to instead of instantiating it as an ImageStyle to instantiate a subclass (e.g., DynamicImageStyle), where the subclass overrides
getPathToken()from hashing$this->id()to instead hashing just the part of$this->id()before--.Comment #27
larowlanDiscussed this in a call with @effulgentsia, particularly the need for the dynamic nature rather than static image styles.
The desire for this is coming from feedback collated by Lauri that configuring this correctly is difficult and arduous.
I pointed out that some of our sites have 100+ image styles and @penyaskito pointed out that Drupal CMS has 55. I was trying to illustrate that having a lot of image styles is common. Alex rightly pointed out that this illustrates the pain point - which I agree. We have cli tooling to get around but this is a attempting a simpler approach.
that sounds like a good idea, this class could also override ::flush and deal with the GC/cleanup. I wonder if we could explore storing third-party settings on the image style rather than relying on magic xb-- naming. This could even hold the dynamic widths so that in theory we could have an extension point. Doesn't need to happen now, but I think trying to do this in a way that could have a path back to core that solves the N image styles proliferation issue should be a long term goal.
So +1 for going down the subclass approach.
Comment #28
wim leersClosed #3503624 as a duplicate of this, see #3503624-4: Create global 'image' component roughly based on NextJS's image component.
Comment #29
wim leersWon't this mean this will only be available for server-side rendered components? So it won't be available for code components (
JavaScriptComponents) or really just … any component that also happens to use images? Doesn't that violate ?→ ah explained later in the issue summary: , but no plan in place yet.
This sounds wrong to me; if it must be a Twig function, then we're essentially saying we're deviating from https://nextjs.org/docs/pages/api-reference/components/image, which contradicts the earlier statements in the issue summary. Next.js' "Image" component declares everything statically and auto-generates an appropriate URL.
(Which means I'm apparently stating almost the same as @catch >2 months ago in #10.)
@catch in #16 on April 17:
Here I have some good news:
Componentversions. The metadata that impacts the version hash (or put differentlY: when the hash changes) could be updated (current implementation stems from #3528362: Deterministic Component version hash should depend not only on source-specific settings, but also slots + explicit input schema) could be updated to include the image restrictions imposed by the component developer.Componentversion is used in which component tree (in both config entities and in which revision of which content entity's XB field component tree).@tonypaulbarker in #17: first you write
which I think everybody would agree with. But then: , after which you list 7 important things content editors must be able to control.
To me this reads like a contradiction. Especially without designs, wireframes or even just links to prior art elsewhere. Could you please elaborate?
@justafish in #18: the issue summary was updated to be more concrete, which is great, but it seems to have descoped 3 items ("we should also consider the following"): CDN support, focal point and DDoS protection. Was that the intent?
@tonypaulbarker in #24: → wouldn't this steer us away from the basic premise of the issue summary, aka striving for "similar to or even reusing" Next.js' Image? The fundamental premise there is the fact that the URL is generated by the component:
https://nextjs.org/docs/app/api-reference/config/next-config-js/images#e...
Also, why is this not essentially the same as DDoS protection which @catch already talked about in detail over at #16?
@fago in #25: AFAICT https://image.nuxt.com/get-started/providers is basically the same as https://nextjs.org/docs/app/api-reference/config/next-config-js/images#e...? Both are basically "generate a URL and let whichever server that points to parse its instructions from the URL".
@effulgentsia in #26: Unsure about many things in that comment, but very strongly agreed with your opposition against adding a file ID to the "image" prop shape, and even more pleasantly surprised and intrigued by your
uri-templateproposal! 🤩I don't understand yet what the proposed "group of variations" means/contains. It sounds a lot like a
ResponsiveImageStyle(i.e. with a bunch of predefinedImageStyles), perhaps namedDynamicResponsiveImageStyle(orImageStyleGroupor … ), but:without the
ImageStyleconfig entities; instead with the contents of those config entities stored directly in this new config entity type — these are what determine what derivatives can be generatedwith a single
itok-esque anti-DDoS URL query arg value perDynamicResponsiveImageStyle, i.e. the sameitokfor all derivativesBut this too appears to contradict what the issue summary wrote about doing something similar to or even re-using https://nextjs.org/docs/pages/api-reference/components/image? 🤔 How could a pure client-side component generate such a URL? It can neither know what "groups" (
DynamicResponsiveImageStyles) exist, nor could it generate an appropriateitok-esque value?Or is the idea that just like for Next images, this functionality would provide configuration options that each image-consuming component must respect — so that could then be:
xb_srcset())<head><head>(That was perhaps obvious to all of you, but it wasn't to me 😇)
Comment #30
catchIf the rendered image size is changed by an SDC - say it changes from image and description side by side to top and bottom and then the image becomes full width instead of half width, then I would assume that's a purely presentational change that should be immediately reflected in all cases that the component is rendered? Otherwise you could potentially have mismatching versions shown even on the same page depending on when they were last saved?
Comment #31
wim leers#30: correct, that is the current reality. See
\Drupal\experience_builder\ComponentSource\ComponentSourceBase::generateVersionHash(). That only looks at:No Twig/markup changes can affect it.
I was just contemplating that it would be possible to either:
*.component.yml" in SDC terminology)Comment #33
wim leersAFAICT, this is almost same problem space as https://www.lullabot.com/articles/decoupled-drupal-hard-problems-image-s....
Quoting from @e0ipso's article:
we need to replace "front-end consumer" with "component (SDC, code component …)" and we actually do know which components are present on a site.
The way that was solved:
Which I believe applies here, too. But instead of the consumer/client (for XB: "component") needing to express all image styles they need, here we'd only need the subset of image styles up to a width that may be smaller than the maximum width configured for the site.
See @lauriii's in #5. Combined with Next.js' Image's
deviceSizes, that'd then result inbeing limited to
or, if the image was limited to 800, then it'd have been limited to:
If we apply @effulgentsia's #26, then I think Alex meant that:
type: object, $ref: json-schema-definitions://experience_builder.module/imageright now — proposal to improve that at #3530351: Decouple image+video (URI) shape matching from specific image+video file types/extensions)Fileentity for an image field, aFileentity for or for an image media entity, or the default image shipped with an SDC) would result in the same 8 derivatives (not necessarily the same URLs, but presumably)front,left,right,backprops, and make thefrontone have a much bigger/more prominent image.type: string, format: uri-templatenear the end of #26; so that you would for a component with limitations imposed by the component developer you'd end up not withbut with something like this (still figuring out how to use URI templates in JSON Schema, but there is a great package for PHP: https://uri.thephpleague.com/uri/7.0/uri-template/ — which I worked on with @gabesullice for AM:A at https://git.drupalcode.org/search?search=uri-template&project_id=63588&s...):
… which would:
{path_to_image}would be replaced (pointing to the selected image) and{itok}(as described by @effulgentsia in #26, with oneitokfor ALL 8 ), but NOT{width}{width}, the client side (or the Twig equivalent) receives the set ofdeviceSize(or however we name our equivalent), which then enables the client to trivially generate all URLs pointing to all <=8 optimized image URLs.itok+ the path to the selected image)This means:
itok, and refuse any widths that are NEITHER in the (for now hardcoded) global/defaultdeviceSizesNOR the max of250(x-image-max-width)P.S.: @justafish indicated that the image style infrastructure only supports
Fileentities. But we know we'll have to make non-Fileentity files work as well, for example default images included in SDCs. I know for sure that this is technically possible, because I had to do this too in my https://drupal.org/project/cdn module, and there I have a route with this path:/cdn/ff/{security_token}/{mtime}/{scheme}. But we'll need be really careful: it's easy to make mistakes — core got it wrong too, seehttps://www.drupal.org/sa-core-2023-005.Comment #34
tonypaulbarker@wim leers
In the absence of some designs and wireframes just for now I would recommend looking at Wix Studio image editing tools (see https://support.wix.com/en/article/studio-editor-editing-and-customizing... ), iPhone native image editing for UI of image adjustments. These UIs are intuitive and easy to use even if the Wix optimisation is poor on the front end. iPhone gives us an easy way to make adjustments and then save adjustments to the original 'entity' or to create a clone.
Cropping is the real pain point. In a recent focus group, several people told us that they use Photoshop or similar to pre-crop their images instead of using Image Widget Crop because they find Drupal too confusing and they can't tell what crop will be used in what context.
Adjusting brightness and contrast in an image style is of little use to an editor - these operations should be able to be set on a per image basis within the media library.
I think that there are some operations where we should be able to generate a clone of a media entity, for example @marcus_johansson wonderful work on image to image AI transformations https://www.drupal.org/project/ai/issues/3531212 https://www.youtube.com/watch?v=ekyu52ARPdU - given the operation is available to our site, we should be able to click to process the image at the point of upload and save to a clone as well as the original if we want.
Images used in the pages of content are different to teasers used in views. Giving people control of instances where views is used is much more difficult. But, if we know that a content context requires a 16:9 image we need a way for editors to intuitively be able to control a 16:9 crop at the point of placing it in the content.
What I envisage is that we give the impression of transforming by leaving the original media entity intact and referencing the entity and / or file from a transformation entity that contains the transformation data and can in some cases - like the AI transformation where processing is too heavy to do on the fly - also save the derivative as a file.
If combined with developers setting expected / allowed aspect ratios (or other transformations) on media fields this could make it easy for editors to crop, maintain consistency and also help with the views teasers scenario. This doesn't quite work when there is art direction changing the dimensions of an image at different breakpoints - but could be extended to requiring all the crops needed for that art direction.
Comment #35
wim leersJust had a ~45 min call with @balintbrews & @effulgentsia about this, and a LOT has been cleared up by it! Much in my last comment #33 is wrong, because I didn't know about 2 key decisions/assumptions not explicitly stated in this issue yet. Witht hem #26 makes a lot more sense:
image_desaturate(grayscale),image_rotateetc. effects, because we just do them in CSS nowadays 🎉On top of this, per @effulgentsia, we can just hardcode the conversion to
.webp, because that is (sufficiently) universally supported.Then, we modify the existing image shape like this:
… to convey that the server should provide a string like
/sites/default/files/styles/xb--{width}/public/2025-06/foo.jpg.webp?itok=1Rl59WAb(which is the image URL template for an image candidate string forsrcset), where the only further information needed on the client side is thedeviceSizes(which can be passed viadrupalSettings).Then the computed property that you and I discussed earlier today, @justafish (which you haven't yet pushed at this time), does start to make sense (unlike what you and I thought earlier today).
That means all other considerations in the issue summary are descoped per @effulgentsia. Including cropping and everything else listed in #34.
Comment #36
wim leersRetitling for narrowed scope.
Comment #37
tonypaulbarker@wim leers
A lot of things are encouraging here but this I'm not so sure about.
A consideration is how will this appear in search engines like Google shopping and Google images or LinkedIn or Facebook ads?
What's in that ~5% and does it matter?
Comment #38
catchOn webp I don't think it needs to be configurable for editors, but we just added #3510582: AVIF conversion with WEBP fallback to core which uses avif if you have it (server side, all browsers that Drupal supports support it already), webp if you don't.
Comment #39
wim leersCan't we use only AVIF? The browser support difference is negligible: http://caniuse.com/avif (93.57%) vs https://caniuse.com/webp (95.62%).
Comment #40
catch@wim browser support is fine, but server-side support is not. https://www.drupal.org/node/3348348 and the related issue has some details. #3510582: AVIF conversion with WEBP fallback falls back to webp based on whether the environment supports AVIF or not. This is the opposite problem to what we had when we adopted webp when server support was fine but browser support wasn't for a long time (mostly due to old OSX versions).
Comment #42
balintbrewsI created #3532044: Image component for code components for the frontend counterpart and drafted the initial implementation based on our discussion yesterday.
Comment #43
balintbrewsComment #44
wim leersI've now implemented most of @effulgentsia's #26, @larowlan's #27 and my #35.
Remaining:
experience_builder:imageSDC — @justafish is handling thatThe 'image.style.xb_parametrized_width' config does not exist.because almost all kernel tests now need to install XB's config)Comment #45
wim leersForgot to show it works:
(This is from before the 404 in case of an arbitrary width being passed in.)
Comment #46
wim leersBTW, this is essentially hardcoding a single
Adapterplugin into the image field type as a computed property.It is feasible to do an update path later that switches existing component trees over to that.
This is a pragmatic step feasible today; neither the XB UI on the client nor the “component instance form” in the server currently have sufficient adapter support.
Comment #48
larowlanComment #49
effulgentsia commented@lauriii and I discussed this, and based on our discussion I wrote up #3532718: Improve the front-end DX of `<img srcset>`. I don't think we should hold up this issue on that though: that issue can be a followup.
Comment #50
larowlanWill pick up the remaining tasks here tomorrow
Comment #51
effulgentsia commentedTagging this as a beta blocker because currently in 0.x, the
srcproperty of an image prop is always set to the original image with no style applied, which means multi-MB images if uploaded from a typical camera or phone.This issue isn't the only way to solve that problem. Other options include: providing a separate image style selector field along with the media field, or using the image style referenced by the media image type's default view mode's display, or creating an XB view mode for that. Any of those could be potentially good things to add in the future, especially to support "interesting" image styles (ones that do something other than scale an image), but the MR on this issue is close, and solves the basic use case of just wanting responsive widths in the simplest way possible from the user's perspective. Which lets us defer integrating "proper" image style configuration until we've thought through the UX of that more thoroughly.
Comment #52
fagoyes, exactly. I think introducing this concept of pluggable providers/loaders makes sense, since generating all image-size variations is often taken quite some load/compuation-time and many variations on disk of image-heavy sites quickly become huge, would it make sense to add a pugin-type for that? (follow-up material).
Also not sure XB is the right home for this, this seems to be a generally very useful addition.
Comment #53
lauriiiI think we should move this to core (or a separate module) eventually. We can experiment with this in XB until that alternative solution exist.
Comment #54
larowlanDown to 4 open threads and the new responsiveImage.spec.ts playwright test is failing.
Need some answers on some of the threads
Comment #55
wim leersbluntoptimistic: let's see if we still need the tweaks this was making, if we do, I'll restore them. The thing is that #3526907 removed the test detection…. 🤞Comment #56
wim leersOne major change I made: the way interesting
battleorder of operations that differs between Recipes and regular config importing/module installing 👹👻 See the rabbit hole thread with a lot of detail about how I ended up with something IMHO better, and @larowlan +1'd 😊In case you're wondering why I named it
ParametrizedImageStyle: https://git.drupalcode.org/project/experience_builder/-/merge_requests/9...@jessebaker is fixing the last (Playwright) test failure due to a conflict with something he just landed, which I resolved incorrectly 🙈
See you in the follow-up: #3533563: Store allowed widths for xb_parametrized_width in third-party settings OR a separate simple config.
Comment #58
wim leersCrediting Jesse :)
Comment #60
wim leers🚢 🥳
Thanks, everyone! Happy weekend! 😊
Comment #61
mglamanThis may have fixed a bug but also broke a nice feature with Recipes using Code Components.
It was nice not having to provide the component config for code components within a recipe.
Comment #62
wim leers@mglaman shared in chat that he found this work-around:
Comment #63
wim leersSomehow #3533563: Store allowed widths for xb_parametrized_width in third-party settings OR a separate simple config was not yet a related issue here, odd.
Comment #64
neha_bawankar commentedTested changes on branch 0.x, for following scenarios:
Scenario
Result
Status
srcsettag is not available inimgReported : Automated
<img srcset>generation not generated for images of smaller dimensionsfor failing test casesComment #65
wim leersSo #64 says this caused a regression, clarified it now: #3535153: Regression from #3515646: small images are pixelated because they're scaled to 100% of the container.
Thanks!
Comment #66
wim leersSee #3532044-30: Image component for code components, there's a silly thing we overlooked here 😇