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

Resizing images #43

Closed
denis-sokolov opened this issue Nov 26, 2018 · 21 comments
Closed

Resizing images #43

denis-sokolov opened this issue Nov 26, 2018 · 21 comments

Comments

@denis-sokolov
Copy link

Would you consider resizing images to be a part of the design goals of this module? It would be very convenient to keep original images in the project directory and in the importing code specify require('./images/my-photo.jpg?resize=1000px')}. From my point of view, resizing images down is a similar semantically operation as minifying with mozjpeg. What do you think?

@cyrilwanner
Copy link
Owner

Hi @denis-sokolov
Thank you for your issue and suggestion! I like the idea and think it would fit well into this plugin. But I have to research a bit first, how this would be possible with webpack. I'll update you as soon as I found something or even implemented it :)

@denis-sokolov
Copy link
Author

In the last days I have put in quite a bit of effort to make responsive-loader work with my Next.js app, but now that it does it works mostly great, although I do miss out on some of the features of next-optimized-images.

@cyrilwanner
Copy link
Owner

Thanks for the link, I didn't know this loader yet. I think it can be supported and combined with the other parts of next-optimized-images, I'll try it out in the next days!

@secretlifeof
Copy link

Hello @denis-sokolov
How did you manage to integrate responsive-loader? I tried but the outputPath and publicPath don't seem to work correctly. I am looking forward to this integration.

// next.config.js

module.exports = {
  webpack: (config, {}) => {
    config.module.rules.push({
      test: /\.(jpe?g|png)$/i,
      loader: 'responsive-loader',
      options: {
        adapter: require('responsive-loader/sharp'),
        placeholder: true,
        placeholderSize: 50,
        outputPath: 'static/images/',
        publicPath: '_next/static/images/'
      }
    })

    return config
  }
}

@denis-sokolov
Copy link
Author

I got the responsive-loader to work with hacks I don’t understand. There is something with the interaction between /_next, /static, and __webpack_public_path__. Still, the following worked for me.

// Webpack config:
config.module.rules.push({
  test: /\.(jpg)$/,
  use: [
    { loader: __dirname + "/lib/Images/image-fixing-loader" },
    {
      loader: "responsive-loader",
      options: {
        adapter: require("responsive-loader/sharp"),
        // Warning, dark magic here and in image-fixing-loader
        name: "static/[name]-[hash].[ext]",
        sizes: [210, 420, 700, 1200, 2000],
        placeholder: true,
        placeholderSize: 50
      }
    }
  ]
});

// image-fixing-loader:
module.exports = function loader(content) {
  return content.replace(/__webpack_public_path__/g, JSON.stringify("/_next/"));
};

@joebanks10
Copy link

Are you able to use responsive-loader with next-optimized-images? I'm looking for a solution that combines image resizing and compression.

@denis-sokolov
Copy link
Author

I’m not using them together, but the responsive-loader already does resizing for you! See the sizes option. The images as included on the page will be loaded just the right size. If you also want to allow the user to click on an image to open the full version, you can link to any of the resized versions at your will, since the imported image in your code has a .images property, which is an array of all the resized versions.

@cyrilwanner
Copy link
Owner

A short update from my side: I was able to add support for the responsive-loader to next-optimized-images yesterday. I'm now just waiting for responsive-loader to publish the newest version to npm (which doesn't require the __webpack_public_path__-hack anymore). If that doesn't happen until the end of the weekend, I can maybe also publish a canary version of next-optimized-images with this hack, but I'd like to do it the clean way in the "real" (or production ready) version :)
But it won't take long until it is supported out of the box ;)

@joebanks10
Copy link

That's great to hear! As I understand it, responsive-loader resizes images and provides a srcset value, but it does not compress/optimize images, so that's why it'd be ideal to combine it with next-optimized-images. Is that right?

@cyrilwanner
Copy link
Owner

cyrilwanner commented Dec 16, 2018

Version v2.2.0 has just been published which now supports image resizing.
If you had responsive-loader previously installed, make sure to also update it to v1.2.0 (earlier versions won't work correctly). Also, responsive-loader requires you to either install jimp (node implementation, slower) or sharp (binary, faster).

After adding the ?resize query param, you can pass in any other available param of the responsive-loader to resize your image.
Example:

const oneSize = require('./images/my-image.jpg?resize&size=300');
const multipleSizes = require('./images/my-image.jpg?resize&sizes[]=300&sizes[]=600&sizes[]=1000');

export default () => (
  <div>
    {/* Single image: */}
    <img src={oneSize.src} />

    {/* Source set with multiple sizes: */}
    <img srcSet={multipleSizes.srcSet} src={multipleSizes.src} />
  </div>
);

I hope that it works as you have expected, otherwise please tell me :)

@cyrilwanner
Copy link
Owner

That's great to hear! As I understand it, responsive-loader resizes images and provides a srcset value, but it does not compress/optimize images, so that's why it'd be ideal to combine it with next-optimized-images. Is that right?

responsive-loader actually already compresses images (not with imagemin but with either jimp or sharp) on its own when resizing them (you can specify a quality in the options). So if you want to resize all your images, you can also just use responsive-loader without next-optimized-images (although it is probably easier to add to a next.js project).
The main reason I added support for it is that you now can have both, next-optimized-images and responsive-loader. Without this support, you wouldn't be able to use both in your projects as both register loaders for .jpg and .png files.

So as of now, you can resize some of your images (with the ?resize query param and responsive-loader) and still use all other features of next-optimized-images (optimizing, gif, svg and webp support, lqip, dominant colors, etc..) in your project for other images. This gives you maximum flexibility and available features.

Just a small note about compressing of resized images: responsive-loader provides a quality option but it looks like it only gets applied to jpeg images and not png. There is maybe a way to also optimize png images (and also other formats which responsive-loader doesn't support, like webp) with a custom adapter. But I currently don't have much time (end of the year, also end of the semester so exams start soon), but I'll explore this option in the semester break. Until then, you have the same features as you would have when just using responsive-loader out of the box.

@denis-sokolov
Copy link
Author

I’ve tried it out and it works exactly as it should.

Small not, but while I have to manually add ?resize to every single require, this is really unappealing. Do you think it would make sense to add a configuration option to enable resize feature for all images by default? More generally, I’d love to set a “default query string” for all require calls.

@cyrilwanner
Copy link
Owner

Hi @denis-sokolov, thank you for trying it out!
I don't know if a default query string for all imports would be possible and if it really makes sense because next-optimized-images would then have to do all work which webpack normally does.
But yes, I think your other proposal should be possible (to use the responsive-loader per default for all jpg & png files). I'll try this out.

To understand your use-case better, do you use mostly the same sizes (so you define them in the loader options and then only add ?resize to all your images) or do most of your images have a different size and you mostly define them in the resource query (?resize&size=300)?

@denis-sokolov
Copy link
Author

Sorry for misleading. Yeah, I meant a default query for images only. In my use-case I use the same sizes, but lots of pictures. I configure the sizes once and require pictures everywhere:

loader: "responsive-loader",
options: {
  adapter: require("responsive-loader/sharp"),
  sizes: [210, 420, 700, 1200, 2000],
}

<MyBlock image={require("./images/4.jpg")} />
<MyBlock image={require("./images/5.jpg")} />
<MyBlock image={require("./images/6.jpg")} />     

I would like to be bold and say this is the most popular use-case. At the end of the day, the sizes is an implementation detail. What I want displayed in my UI is a particular image. Some browsers will load a smaller version of the image, but this is not a concern of my design, it’s a technological performance detail.

@joebanks10
Copy link

For our use case, we don't use the same sizes for all images. We'd size them based off the resource query.

@cyrilwanner
Copy link
Owner

So, I think that I found an improvement for both use cases.

Specifying the size with resource queries

When you want to resize single images with the query param (e.g. my-image.jpg?size=300 or my-image.jpg?sizes[]=300&sizes[]=500), you don't need the ?resize prefix anymore (but it still works if you want it). Although if you need any other query of responsive-loader, you still need the resize prefix.

Let responsive-loader handle all JPEG and PNG images per default

If you want all images to be resized with a global config (as of your use case @denis-sokolov), you can now overwrite the default loader with the defaultImageLoader option.
All JPEG and PNG images will then directly be forwarded to the responsive-loader. But as this loader is more restrictive, the query params of next-optimized-images won't work anymore (e.g. responsive-loader can't handle inlined images etc.). But for all other types (SVG, WEBP and GIF), img-loader will still be used and the queries are still available there.

I hope that this improves the experience for both use cases. Both changes are available in version 2.3.0 which has just been published to npm.

@denis-sokolov
Copy link
Author

This does work great and I have now switched to next-optimized-images and deleted the hacks from my code! \o/ Thank you.

@jonasIv
Copy link

jonasIv commented Dec 19, 2018

Thanks for putting in all the work.
Is there currently a way to resize WEBP images through a query param? If only responsive-loader would support WEBP...

My use-case is that I have a JPEG image and I want to provide the following to a picture element:

  • different sizes (for different devices and different device-pixel-ratios)
  • different formats (JPEG, WEBP)
  • all of them optimized
  • placeholder image

@cyrilwanner
Copy link
Owner

Hi @jonasIv
Your use-case makes total sense and I guess other devs would also like that.
Unfortunately, this isn't possible at the moment. Mainly because responsive-loader doesn't support WEBP.
Implementing it directly in next-optimized-images would be too much work and I also don't think that it would be the correct place to do it. It would be way easier in the responsive-loader itself which also has the benefit that it could be used for non next.js projects.

I just saw that there is an issue in responsive-loader for exactly your request: dazuaz/responsive-loader#42 and even a PR which had it implemented: dazuaz/responsive-loader#58
Unfortunately, there hasn't been recent activity on it, but we could maybe ask there if this feature is still planned or they need help.
If this gets implemented in responsive-loader, I will of course also adapt next-optimized-images (if necessary).

@cyrilwanner
Copy link
Owner

I'll close this for now as the main functionality has been implemented. I'll keep an eye open for the webp implementation in responsive-loader and update this package as soon as they added support for webp images.

@vgorloff
Copy link

vgorloff commented Feb 4, 2021

I had an issue of getting "responsive-loader/sharp" working in "next-optimized-images" (v2.6.2) in year 2021. Here is what I did in order to make it working.

  1. "responsive-loader" should be v2.0.0 when using with "next-optimized-images" (v2.6.2). Otherwise you will get an error about missed module/file from responsive-loader/sharp.

  2. Change next.config.js like below:

    const withPlugins = require("next-compose-plugins");
    const optimizedImages = require("next-optimized-images");
    
    module.exports = withPlugins([
      [
        optimizedImages,
        {
          // See: https://github.com/cyrilwanner/next-optimized-images#responsive
          responsive: {
            adapter: require("responsive-loader/sharp"),
            sizes: [300, 320, 640, 960, 1200, 1800, 2400],
            placeholder: true,
            placeholderSize: 40,
          },
        },
      ],
    
      // your other plugins here
    ]);
  3. Clean ./.next folder (e.g. rm -rf ./.next). Otherwise NextJS may pick not desired cached config.

  4. Use image in JSX

    import multipleSizes from './media/1-OpenProject.png?resize'
    
    export default function App() {
      return (
        <>
          <img srcSet={multipleSizes.srcSet} src={multipleSizes.src} />
        </>
      );
    }
  5. Make a static build (e.g. rm -rf ./build && next build && next export -o build)

As result you have HTML generated as below:

<img
  srcset="
    /_next/static/images/1-OpenProject-300-8bd205d8fa6712a27d68d10d2fa96041.png   300w,
    /_next/static/images/1-OpenProject-320-149721ce42ee5cc54bb9969e9527f858.png   320w,
    /_next/static/images/1-OpenProject-640-43595f0f9dc6d2e17b557d7def96c796.png   640w,
    /_next/static/images/1-OpenProject-960-1c9adea9078cf40f468c7a2a5d4041e0.png   960w,
    /_next/static/images/1-OpenProject-1200-0f021a08f50c63c7b0f47c0f6eb921a6.png 1200w,
    /_next/static/images/1-OpenProject-1800-70b7d4f7b788d3418cf7729e764e9951.png 1800w,
    /_next/static/images/1-OpenProject-2400-5bbb6e6fcc73108fff95a32dc9cf346b.png 2400w
  "
  src="/_next/static/images/1-OpenProject-300-8bd205d8fa6712a27d68d10d2fa96041.png"
/>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants