Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Image Loaders #18606

Closed
ricokahler opened this issue Nov 1, 2020 · 5 comments · Fixed by #20216
Closed

Custom Image Loaders #18606

ricokahler opened this issue Nov 1, 2020 · 5 comments · Fixed by #20216
Assignees
Milestone

Comments

@ricokahler
Copy link

ricokahler commented Nov 1, 2020

Feature request

The next image component should allow for custom image loaders.

Currently only a few are supported with no way to add a custom one in userland.

Is your feature request related to a problem? Please describe.

In the headless CMS space, there are a good amount of providers that include an image service that transforms images on the fly.

(These were all sponsors of next conf too lol)

Describe the solution you'd like

Edit: I no longer think this is the right solution. See here instead #18606 (comment)

Something like this:

// my-image-service.ts
import { ImageLoader } from 'next';

const imageLoader: ImageLoader = ({ root, src, width, quality }) => {
  // ...
}

export default imageLoader;
// next.config.js
module.exports = {
  // ...
  images: {
    loader: require.resolve('./my-image-service'),
    path: 'https://example.com/myaccount/',
  },
}

Describe alternatives you've considered

I could PR the service I want to be added but I don't think that will scale well. Also adding this feature can make it so y'all can remove the built-in loaders from the image component and reduce bundle size a bit.

@ricokahler
Copy link
Author

ricokahler commented Nov 1, 2020

Edit: see my next comment instead: #18606 (comment)


I also have an idea on how to implement this.

Last I checked, non-literal imports in webpack aren't possible so we'd have to figure out a way to get the user's loader into the bundle inside the image component. I think the simplest solution for this would to defer to the bundler and inject via an alias.

In the image component, we can require a dummy file that we can resolve to if no custom loader is provided.

// /packages/next/client/image-custom-loader.ts
import { ImageLoader } from 'next';
export default null as ImageLoader | null;

Then in the image component, we can use the import as if it were either a user loader or just null.

// /packages/next/client/image.tsx
import imageCustomLoader from './image-custom-loader';

// use the loader

In the build, we conditionally alias that dummy file in webpack-config.ts

export default async function getBaseWebpackConfig(/* ... */) {
  // ...
  const resolveConfig = {
    const IMAGE_LOADER_ALIAS = /* ... */;
    const isCustomLoader = /* ... */;

    alias: {
      ...(isCustomLoader && { [IMAGE_LOADER_ALIAS]: config.images.loader }),      
    },
  };

  // ...
}

Additionally, we can create a file for each built-in loader and alias to a different destination built-in loader to save a few bytes.

@Timer Timer added this to the 10.x.x milestone Nov 1, 2020
@matamatanot
Copy link
Contributor

ref #18450

@ricokahler
Copy link
Author

So I've thought about this a bit more and I want to change the solution I'd like.

I think y'all should just expose an imageLoader prop on the component level. This makes it much more simple to implement (I think?) and it creates a pattern that is more open to various different image loaders on the same site.

For example, if I wanted an image component that works for Sanity images, I could create a SanityImage component that composes over the next image component.

// SanityImage.js
import Image from 'next/image';

const sanityImageLoader = ({/* ... */}) => {
  // ...
};

function SanityImage(props) {
  return <Image {...props} imageLoader={sanityImageLoader} />
}

export default SanityImage;

@mpoisot
Copy link

mpoisot commented Dec 15, 2020

It looks like the official code is getting developed in #19325.

@Timer Timer modified the milestones: 10.x.x, iteration 15 Dec 15, 2020
@styfle styfle linked a pull request Dec 15, 2020 that will close this issue
@styfle styfle linked a pull request Dec 15, 2020 that will close this issue
@styfle styfle linked a pull request Dec 15, 2020 that will close this issue
@Timer Timer self-assigned this Dec 31, 2020
@kodiakhq kodiakhq bot closed this as completed in #20216 Jan 5, 2021
kodiakhq bot pushed a commit that referenced this issue Jan 5, 2021
This is a #19325 reconfigured to support a loader passed in via a `loader` prop on the Image component, rather than using a config-based approach.

The idea is that applications wanting to use a custom loader will create a wrapper element for the  image component that incorporates that loader. See a simple example of this pattern in the integration tests. 

This solution is similar to the one prototyped by @ricokahler in #20213 and described at #18606 (comment)

---

Closes #19325
Fixes #18606
@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
5 participants