How to load image files with webpack file-loader

I had an issue uploading images to my React JS project. I was trying to use the file-loader to load the images; I was also using Babel-loader in my react.

I used the following settings in the webpack:

{test: /\.(jpe?g|png|gif|svg)$/i, loader: "file-loader?name=app/images/[name].[ext]"},

This helped load my images, but the images loaded were kind of corrupted. Then after some research I came to know that file-loader has a bug of corrupting the images when babel-loader is installed.

Hence, to work around the issue I tried to use URL-loader which worked perfectly for me.

I updated my webpack with the following settings

{test: /\.(jpe?g|png|gif|svg)$/i, loader: "url-loader?name=app/images/[name].[ext]"},

I then used the following command to import the images

import img from 'app/images/GM_logo_2.jpg'
<div className="large-8 columns">

      <img  style={{ width: 300, height: 150 }} src={img} />
</div>

Install file loader first:

$ npm install file-loader --save-dev

And add this rule in webpack.config.js

           {
                test: /\.(png|jpg|gif)$/,
                use: [{
                    loader: 'file-loader',
                    options: {}
                }]
            }

Regarding problem #1

Once you have the file-loader configured in the webpack.config, whenever you use import/require it tests the path against all loaders, and in case there is a match it passes the contents through that loader. In your case, it matched

{
    test: /\.(jpe?g|png|gif|svg)$/i, 
    loader: "file-loader?name=/public/icons/[name].[ext]"
}

// For newer versions of Webpack it should be
{
    test: /\.(jpe?g|png|gif|svg)$/i, 
    loader: 'file-loader',
    options: {
      name: '/public/icons/[name].[ext]'
    }
}

and therefore you see the image emitted to

dist/public/icons/imageview_item_normal.png

which is the wanted behavior.

The reason you are also getting the hash file name, is because you are adding an additional inline file-loader. You are importing the image as:

'file!../../public/icons/imageview_item_normal.png'.

Prefixing with file!, passes the file into the file-loader again, and this time it doesn't have the name configuration.

So your import should really just be:

import img from '../../public/icons/imageview_item_normal.png'

Update

As noted by @cgatian, if you actually want to use an inline file-loader, ignoring the webpack global configuration, you can prefix the import with two exclamation marks (!!):

import '!!file!../../public/icons/imageview_item_normal.png'.

Regarding problem #2

After importing the png, the img variable only holds the path the file-loader "knows about", which is public/icons/[name].[ext] (aka "file-loader? name=/public/icons/[name].[ext]"). Your output dir "dist" is unknown. You could solve this in two ways:

  1. Run all your code under the "dist" folder
  2. Add publicPath property to your output config, that points to your output directory (in your case ./dist).

Example:

output: {
  path: PATHS.build,
  filename: 'app.bundle.js',
  publicPath: PATHS.build
},

Alternatively you can write the same like

{
    test: /\.(svg|png|jpg|jpeg|gif)$/,
    include: 'path of input image directory',
    use: {
        loader: 'file-loader',
        options: {
            name: '[path][name].[ext]',
            outputPath: 'path of output image directory'
        }
    }
}

and then use simple import

import varName from 'relative path';

and in jsx write like <img src={varName} ..../>

.... are for other image attributes