Image component for code components

Created on 24 June 2025, 10 days ago

Overview

Provide an Image component for code component authors that can be used to output <img> tags with an automatically generated srcset attribute leveraging the backend support added in Add automated image optimization to image component Active .

Proposed resolution

Build a wrapper component around next-image-standalone with a loader function that works alongside the data added in Add automated image optimization to image component Active .

Make this component available with the following import statement:

import Image from "@/lib/Image";

User interface changes

n/a

Feature request
Status

Active

Version

0.0

Component

Theme builder

Created by

🇳🇱Netherlands balintbrews Amsterdam, NL

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @balintbrews
  • 🇳🇱Netherlands balintbrews Amsterdam, NL
  • 🇺🇸United States effulgentsia

    This is great! I wonder if the following would be an improvement or not...

    What if instead of a wrapper component, Image from "@/lib/Image", people used next-image-standalone directly, but we gave them a utility function for getting the loader function to pass in? For example:

    import Image from "next-image-standalone";
    import {useImageLoader} from "@/lib/utils";
    
    const Cover = ({ image }) => {
      const loader = useImageLoader(image.srcSetCandidateTemplate);
      return (
        <div>
          <Image 
            src={image.src}
            alt={image.alt}
            width={image.width}
            height={image.height}
            loader={loader}
          />
        </div>
      );
    };
    
    export default Cover;
    

    Would the above be more idiomatic in that it would be clearer that people are using essentially the stock Nextjs image component rather than "who knows what else @/lib/Image does"? Or is the @/lib/Image wrapper idea better because it gives us room to add other customizations, like adding some defaults to the config prop?

  • 🇳🇱Netherlands balintbrews Amsterdam, NL

    #4: That's a great idea! It's in line with how we chose to provide @drupal-api-client/json-api-client and SWR instead of a custom wrapper. More idiomatic, as you said. For both we add minimal customization and convenience, but we still keep their original names in the module specifiers. So I think we can do the same if we choose to provide next-image-standalone directly, as in, we would still have the room to add customizations, such as defaults to the config prop, as you point out.

    The only potential downside is that a loader function is optional with next/image: it optimizes images using its own backend by default. I wonder if it could be misleading that we provide something called next-image-standalone, which may make it sound like a loader function is optional, just as it is with next/image. Whereas it is required for next-image-standalone, which is written in its README.

    So the way I see it, we need to choose how we would like to communicate what is it that we offer:

    1. "Here is a component that's almost identical to next/image, but you must provide a loader function. For that we also got you covered, here is a loader function that works with our backend."
    2. "Here is a component you can use to output images. All you need is to pass these simple props. For more control, the component also accepts most props next/image does."
  • 🇺🇸United States effulgentsia

    I prefer #5.1 over #5.2 because it's more similar to shadcn's open code approach. It makes the use of next/image (or the next-image-standalone version of it which is just a way to get next/image outside of a Next.js context) ultimately a decision made by the component author (even if we nudge that decision by providing an example), whereas to me it feels like #5.2 would imply that XB is making that decision more heavy-handedly.

  • 🇺🇸United States effulgentsia

    I wonder if it could be misleading that we provide something called next-image-standalone, which may make it sound like a loader function is optional, just as it is with next/image

    Is it reasonable for someone to expect "standalone" (by which is meant: usable outside of Next.js) to have a way of working without providing a loader function?

  • 🇳🇱Netherlands balintbrews Amsterdam, NL

    #6: Love that, let's go with #5.1. It should be straightforward to adjust the MR to that direction.

    #7: I think it depends on how much they understand about what next/image does. What's nice about next/image is that it's not very necessary to fully understand, it mostly just works. Either way, I'm more than okay to let this thought go, and I'm happy with #5.1, a.k.a. #4.

  • 🇫🇮Finland lauriii Finland

    Next.js allows configuring the default loader. I believe this is the most common way of configuring this – it doesn't make sense configuring this per instance unless there's something about a specific instance why it needs to use a different loader.

    I think the ideal experience would we that we have a default loader which just works within the Drupal environment. There could be additional loaders for example to use CDN from a DAM to load an image, which would then potentially have to be loaded from within a component.

    I don't think this goes against the open code approach because this is already an API that the upstream provides, which I also believe is the primary way of configuring this for Next.js applications.

  • 🇺🇸United States effulgentsia

    it doesn't make sense configuring this per instance

    But where to configure it then? There's no equivalent to next.config.js for code components unless we introduce some new convention for code component global JS config. We have a global CSS tab, so maybe adding a global JS tab makes sense?

    I think the ideal experience would we that we have a default loader which just works within the Drupal environment.

    We could do this per #5's answer about adding defaults to config. However, the default loader would still require a per-instance prop (srcSetCandidateTemplate) set that isn't part of the next/image set of props. So #4 could be simplified to:

    import Image from "next-image-standalone";
    
    const Cover = ({ image }) => {
      const {src, alt, width, height, srcSetCandidateTemplate} = image;
      return (
        <div>
          <Image {...{src, alt, width, height, srcSetCandidateTemplate}}/>
        </div>
      );
    };
    
    export default Cover;
    

    However, I don't think <Image {...{src, alt, width, height, srcSetCandidateTemplate}}/> is any friendlier than <Image {...{src, alt, width, height, loader}}/>.

  • 🇺🇸United States effulgentsia

    Some minor DX improvements we can make to #10...

    1. srcSetCandidateTemplate is a mouthful. Perhaps this could just be srcTemplate.
    2. Although in next/image, the loader prop is always a function, perhaps next-image-standalone could allow it to be either a function or a string, and if it's a string, convert it to a function that replaces {src}, {width}, and {quality} in the string.

    Doing both of the above would mean the #10 code would become:

    import Image from "next-image-standalone";
    
    const Cover = ({ image }) => {
      const {src, alt, width, height, srcTemplate} = image;
      return (
        <div>
          <Image {...{src, alt, width, height}} loader={srcTemplate} />
        </div>
      );
    };
    
    export default Cover;
    
  • 🇫🇮Finland lauriii Finland

    But where to configure it then? There's no equivalent to next.config.js for code components unless we introduce some new convention for code component global JS config. We have a global CSS tab, so maybe adding a global JS tab makes sense?

    Maybe we don't have to introduce a configuration at this point so long as we have set the correct default? I would imagine in Drupal this is something that a module may want to change, similar to what the CDN module is doing.

    We could do this per #5's answer about adding defaults to config. However, the default loader would still require a per-instance prop (srcSetCandidateTemplate) set that isn't part of the next/image set of props.

    Why do we need srcSetCandidateTemplate to be passed per instance? Why would I as the user of that component care about it? I would have no idea based on that prop name why it's there and why I'd have to pass it to the function. Tried to look at the code in the MR and in Add automated image optimization to image component Active but it still wasn't clear what it is.

  • 🇺🇸United States effulgentsia

    @lauriii: I think you'll like 📌 Improve the front-end DX of Active .

  • 🇫🇮Finland lauriii Finland

    Proposal on that issue looks great! 👏

  • 🇺🇸United States effulgentsia

    Tagging as beta blocker for the same reason as #3515646-51: Add automated generation .

Production build 0.71.5 2024