6

Here is a codesandbox I would like the image in it to be processed by the sharp module. I want it to do this because I want this module to web optimize all the images in my React project, and to convert them to webm.

Here's the same codesandbox where I attempt to use ImageSharp

Sam
  • 1,765
  • 11
  • 82
  • 176
  • 1
    Thanks for your complete question alongside sandbox, I upvoted you. – AmerllicA May 12 '21 at 22:42
  • When I open you codesandbox link I get this error "Could not find module in path: '' relative to '/package.json'", this is what are you trying to fix? – lissettdm May 17 '21 at 15:38
  • @lissettdm The second sandbox won't work because of the line `'../../react-logo.png?{"outputs":[{"width": 500}]}';` this is how sharp reads images from what I've read though – Sam May 17 '21 at 16:44

2 Answers2

5

You will need to make some changes:

Webpack.config.js

  1. Move sharp-loader to rules:
const HtmlWebPackPlugin = require("html-webpack-plugin");

module.exports = {
  module: {
    rules: [
      ...
      {
        test: /\.(gif|jpe?g|png|svg|tiff)(\?.*)?$/,
        loader: "sharp-loader",
        query: {
          name: "[name].[hash:8].[ext]",
          cacheDirectory: true,
          presets: {
            // Preset 1
            thumbnail: {
              format: ["webp", "jpeg"],
              width: 200,
              quality: 60
            },
            // Preset 2
            prefetch: {
              // Format-specific options can be specified like this:
              format: { id: "jpeg", quality: 30 },
              mode: "cover",
              blur: 100,
              inline: true,
              size: 50
            }
          }
        }
      }
    ]
  },
  devServer: {
    ...
  },
  plugins: [
    ...
  ]
};

If you are using Webpack 5:

Rule.query is deprecated in favor of Rule.options and UseEntry.options.

Home.js

  1. The sharp-loader will transform logo into an array of one object because of this "outputs":[{"width": 500}]. You will need to use the corresponding object:
import React from "react";
import logo from '../../react-logo.png?{"outputs":[{"width": 500}]}';

export default function Home() {
  return (
    <div>
      <h2>Home</h2>
      <img src={logo[0].url} />
    </div>
  );
}

If you are using require.context it works the same way:


import React from "react";

function importAll(r) {
  return r.keys().reduce((curr, key) => ((curr[key] = r(key)), curr), {});
}

const images = importAll(require.context('./images/?{"outputs":[{"width": 200}, {"width": 150}]}', true, /\.png$/));


export default function Home() {
  return (
    <div>
      <h2>Home</h2>
      <img src={images['./react-logo.png'][0].url} />
    </div>
  );
}
lissettdm
  • 12,267
  • 1
  • 18
  • 39
  • I get this when I do that `Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema. - configuration.module.rules[2] has an unknown property 'query'. These properties are valid:` – Sam Jun 16 '21 at 22:57
  • Hi @Sam what version of Webpack are you using? – lissettdm Jun 17 '21 at 14:01
  • This is a fantastic solution! Any idea how one could import multiple images and do the same thing? Say for an image gallery with thumbnails? e.g `importAll(require.context('../../photos', false, /\.(png|jpe?g|svg)$/));` – mortware Jun 28 '21 at 09:54
  • Also - any guidance on getting this to work with webpack 5? – mortware Jun 28 '21 at 12:16
  • 1
    @mortware I added an example using require.context. If you are using Webpack 5 just replace the property query with options. – lissettdm Jun 29 '21 at 19:15
2

I would create a convert.js in root directory to do the file conversion. check sharp docs.

Then in package.json simply add 'node convert' before any script which needs image conversion specially start and build

    "start": "node convert && react-scripts start",
    "build": "node convert && react-scripts build",

Now any time you start local server or build your app, it first converts the images and then does the rest.

Here is a simple example where convert.js only converts a file named 1.jpeg to output.webp with dimension 320x240. It's all up to you how you like to organize image sizes, folders and files in project so that covert.js can find them and properly convert them.

const sharp = require('sharp');
sharp('1.jpeg')
    .resize(320, 240)
    .toFile('output.webp', (err, info) => { });
MHIdea
  • 806
  • 3
  • 8