80

I've been trying to figure out how to dynamically add images via React and Webpack. I have an image folder under src/images and a component under src/components/index. I'm using url-loader with the following config for webpack

    {
      test: /\.(png|jpg|)$/,
      loader: 'url-loader?limit=200000'
    }

Within the component I know I can add require(image_path) for a specific image at the top of the file before I create the component but I want make the component generic and have it take a property with the path for the image that is passed from the parent component.

What I have tried is:

<img src={require(this.props.img)} />

For the actual property I have tried pretty much every path I can think of to the image from the project root, from the react app root, and from the component itself.

Filesystem

|-- src
|   ` app.js
|   `--images
|      ` image.jpg
|      ` image.jpg
|   `-- components
|      `parent_component.js
|      `child_component.js

The parent component is basically just a container to hold multiples of the child so...

<ChildComponent img=data.img1 />
<ChildComponent img=data.img2 />
etc....

Is there any way in which to do this using react and webpack with url-loader or am I just going down a wrong path to approach this?

Damjan Pavlica
  • 21,431
  • 6
  • 55
  • 65
pandorz
  • 803
  • 1
  • 6
  • 4

7 Answers7

99

Using url-loader, described here (SurviveJS - Loading Images), you can then use in your code :

import LogoImg from 'YOUR_PATH/logo.png';

and

<img src={LogoImg}/>

Edit: a precision, images are inlined in the js archive with this technique. It can be worthy for small images, but use the technique wisely.

mlorber
  • 1,356
  • 11
  • 17
  • 1
    I wish to know how to load this image from and API that returns the image. – The Oracle Dec 07 '16 at 15:39
  • 3
    I'm not sure to undertand : if you want the user browser to load the image from an API, it has nothing to do with webpack. Just use a variable (probably defined after a call to the API) as the src. The idea here was to include the image in your sources and webpack archive. (see http://stackoverflow.com/questions/32612912/dynamically-add-images-react-webpack/35454832?noredirect=1#comment56021739_32613874 ) – mlorber Dec 08 '16 at 16:01
  • 1
    Thanks, this works great. The article you cited also mentions at the end if you are using React, then you use babel-plugin-transform-react-jsx-img-import to generate the import automatically. that way you can just write – Hom Bahrani Jun 17 '17 at 14:09
  • Warning: url-loader will inline the image file as a base64 ascii string. This decreases the number of file-accesses needed to load a page, but at the cost of more bytes to download. So, may be better to use file-loader for production. – hrabinowitz Jul 30 '19 at 21:03
  • Yes, images are inlined with this technique. It completely depends on the weight of the file. For small ones (icons mainly for me), it can be worthy. I'll edit the response – mlorber Aug 02 '19 at 07:07
25

If you are bundling your code at the server-side, then there is nothing stopping you from requiring assets directly from jsx:

<div>
  <h1>Image</h1>
  <img src={require('./assets/image.png')} />
</div>
James Akwuh
  • 1,848
  • 1
  • 19
  • 21
  • 3
    This is what I needed to use for my situation where I don't know the image file until the component is built. I pass in an `imageFilename` prop and then use ``. Thanks for posting. – Brendan Moore Feb 18 '17 at 06:32
  • 1
    @BrendanMoore but remember that in your case a bundler (say webpack) will add all images from the `images` folder to the final bundle. – James Akwuh Feb 21 '17 at 12:31
  • 1
    Exactly what I was looking for. Thanks @JamesAkwuh – Samrat Saha Jun 13 '19 at 12:38
  • 9
    If this isn't working, then add .default after require(). Eg: I'm using like `const src = require(`assets/images/${name}`).default;` – master_dodo Aug 29 '20 at 15:30
12

UPDATE: this only tested with server side rendering ( universal Javascript ) here is my boilerplate.

With only file-loader you can load images dynamically - the trick is to use ES6 template strings so that Webpack can pick it up:

This will NOT work. :

const myImg = './cute.jpg'
<img src={require(myImg)} />

To fix this, just use template strings instead :

const myImg = './cute.jpg'
<img src={require(`${myImg}`)} />

webpack.config.js :

var HtmlWebpackPlugin =  require('html-webpack-plugin')
var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')

module.exports = {
  entry : './src/app.js',
  output : {
    path : './dist',
    filename : 'app.bundle.js'
  },
  plugins : [
  new ExtractTextWebpackPlugin('app.bundle.css')],
  module : {
    rules : [{
      test : /\.css$/,
      use : ExtractTextWebpackPlugin.extract({
        fallback : 'style-loader',
        use: 'css-loader'
      })
    },{
      test: /\.js$/,
      exclude: /(node_modules)/,
      loader: 'babel-loader',
      query: {
        presets: ['react','es2015']
      }
    },{
      test : /\.jpg$/,
      exclude: /(node_modules)/,
      loader : 'file-loader'
    }]
  }
}
Marwen Trabelsi
  • 3,951
  • 7
  • 37
  • 71
9

If you are looking for a way to import all your images from the image

// Import all images in image folder
function importAll(r) {
    let images = {};
    r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
    return images;
}

const images = importAll(require.context('../images', false, /\.(gif|jpe?g|svg)$/));

Then:

<img src={images['image-01.jpg']}/>

You can find the original thread here: Dynamically import images from a directory using webpack

Gabriel Esu
  • 101
  • 1
  • 2
6

So you have to add an import statement on your parent component:

class ParentClass extends Component {
  render() {
    const img = require('../images/img.png');
    return (
      <div>
        <ChildClass
          img={img}
        />
      </div>
    );
  }
}

and in the child class:

class ChildClass extends Component {
  render() {
    return (
      <div>
          <img
            src={this.props.img}
          />
      </div>
    );
  }
}
MCSLI
  • 281
  • 4
  • 5
3

You do not embed the images in the bundle. They are called through the browser. So its;

var imgSrc = './image/image1.jpg';

return <img src={imgSrc} />
J. Mark Stevens
  • 4,687
  • 2
  • 10
  • 17
  • 3
    but what if you don't know the image at build time? What if the image URL is composed at runtime? – JBCP Dec 07 '15 at 15:21
  • The goal of the loader you're using is actually to NOT using an url in your code, and being potentially able to embed the image in the archive if it's small enough (better perfs) – mlorber Feb 17 '16 at 11:00
2

here is the code

    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './image.css';
    import Dropdown from 'react-dropdown';
    import axios from 'axios';

    let obj = {};

    class App extends Component {
      constructor(){
        super();
        this.state = {
          selectedFiles: []
        }
        this.fileUploadHandler = this.fileUploadHandler.bind(this);
      }

      fileUploadHandler(file){
        let selectedFiles_ = this.state.selectedFiles;
        selectedFiles_.push(file);
        this.setState({selectedFiles: selectedFiles_});
      }

      render() {
        let Images = this.state.selectedFiles.map(image => {
          <div className = "image_parent">

              <img src={require(image.src)}
              />
          </div>
        });

        return (
            <div className="image-upload images_main">

            <input type="file" onClick={this.fileUploadHandler}/>
            {Images}

            </div>
        );
      }
    }

    export default App;
Rajat Jain
  • 1,155
  • 2
  • 12
  • 28