dynamically import images from a directory using webpack

  • Last Update :
  • Techknowledgy :

It's easy. You can use require (a static method, import is just for dynamic files) inside the render. Like the example below: With the way that I loaded the images the filenames are lost when mapping from the cache object but if you need them you can just use the cache object directly instead as the keys are the filename. Here is a functional component I made in Reactjs to simply show all the images inside the media folder in my project (same level as component) using the webpack docs and some of the answers here.

This is our Splunktool team suggestion ✌, we tried and its working fine
function importAll(r) {
  let images = {};
  r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
  return images;
}
​
const images = importAll(require.context('./images', false, /\.(png|jpe?g|svg)$/));
​
<img src={images['doggy.png']} />
​

Not in ES6. The whole point of import and export is that dependencies can be determined statically, i.e. without executing code.

But since you are using webpack, have a look at require.context . You should be able to do the following:

function importAll(r) {
   return r.keys().map(r);
}

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
3._
render() {
    const {
      someProp,
    } = this.props

    const graphImage = require('./graph-' + anyVariable + '.png')
    const tableImage = require('./table-' + anyVariable2 + '.png')

    return (
    <img src={graphImage}/>
    )
}

Suggestion : 2

After researching more, I realised that require only work with static paths (i.e. a hardcoded path instead of a path stored in a variable). This is because Create-react-app uses Webpack under the hood [1] and as a bundler, you have to specify a context where the images are loaded. One of the main features of webpack’s compiler is to recursively parse all the modules and to build a graph of module dependencies. The definition of context in Webpack is used as a base to resolve paths to modules. For instance, the default context is __dirname. [2] A friend recently asked me a perculiar problem. She was trying to build a carousel that cycles through multiple images in a directory for a React app bootstrapped using Create-react-app.

1._
const imgFolder = './assets/';
const fs = require('fs');

fs.readdirSync(imgFolder).forEach(file => {
   console.log(file); // 'brave.png'
});
2._
const imgFolder = './assets/';
const fs = require('fs');

fs.readdirSync(imgFolder).forEach(file => {
  const img_src = require(`${imgFolder}${file}`);
  return <img src={img_src}/>;
});
3._
require.context(directory, useSubdirectories = false, regExp = /^**\.\/**/)

// To solve the problem
const imgFolder = require.context('./assets/', useSubdirectories = false)
const img_node = images(`./${someVariable}.png`);
return <img src={img_node}/>;

Suggestion : 3

A common need in Remotion is to import assets from dynamic paths. This means that sometimes you don't know the exact path of an asset that should be imported upfront and you want to calculate it during runtime. Let's imagine a scenario where the asset that should be imported is completely unknown and will be read at runtime, for example through an input prop: even if the file exists. This is because Webpack needs to figure out using static code analysis which assets it should bundle and cannot do so.

This can become an unexpected hurdle in Webpack. On this page we collect some tips how to handle dynamic assets.

Take this scenario for an example:

tsximport { Img, useCurrentFrame } from "remotion"export const DynamicImports: React.FC = () => {  const frame = useCurrentFrame();  const img = "./assets/image" + frame + ".png";  return <Img src={require(img)} />;};Copy

may result in:

bashError: Cannot find module './image0.png'
Copy

While the example at the top did not work, Webpack is smart enough to do so if you place your expression inside the require() or import() statement. In this case, Webpack will automatically bundle all .png files in the assets/image folder even if the asset is never used.

The following does work:

tsximport { Img, useCurrentFrame } from "remotion"export const DynamicImports: React.FC = () => {  const frame = useCurrentFrame();  return <Img src={require("./assets/image" + frame + ".png")} />;};Copy

This cannot work because Webpack has no ideas which assets it has to bundle. Therefore the import has to fail. Like above, you can force Webpack to bundle the whole assets folder by putting an expression inside the require() statement:

tsximport { getInputProps, Img } from "remotion"const DynamicAsset: React.FC = () => {  const inputProps = getInputProps(); // {"imageSrc": "img0.png"}  // Works!  return <Img src={require("./assets/" + inputProps.imageSrc)} />;};Copy

Suggestion : 4

I feel like there must be some way to dynamically import all files from a specific directory as their name sans extension, and then use those files as needed. With the way that I loaded the images the filenames are lost when mapping from the cache object but if you need them you can just use the cache object directly instead as the keys are the filename. Here is a functional component I made in Reactjs to simply show all the images inside the media folder in my project (same level as component) using the webpack docs and some of the answers here.

Not in ES6. The whole point of import and export is that dependencies can be determined statically, i.e. without executing code.

But since you are using webpack, have a look at require.context . You should be able to do the following:

function importAll(r) {
   return r.keys().map(r);
}

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
2._
import React from 'react';

const cache = {};

function importAll(r) {
    r.keys().forEach((key) => (cache[key] = r(key)));
}
// Note from the docs -> Warning: The arguments passed to require.context must be literals!
importAll(require.context("./media", false, /\.(png|jpe?g|svg)$/));

const images = Object.entries(cache).map(module => module[1].default);


const MediaPage = () => {
    return (
        <>
            <p>Media Page..</p>

            {images.map(image => (
                <img style={{width: 100}} src={image} />
            ))}
        </>
    );
}

export default MediaPage;

// example with styles just for clarity
return (
    <>
        <p>Media Page..</p>

        {Object.entries(cache).map(module => {
            const image = module[1].default;
            const name = module[0].replace("./","");
            return (
                <div style={{float: 'left', padding: 10, margin: 10, border: '2px solid white' }}>
                    <img style={{width: 100, margin: 'auto', display: 'block'}} src={image} />
                    <p>{name}</p>
                </div>
            )
        })}
    </>
);

I have directory of png country flags named like au.png, nl.png etc. So I have:

-svg - country - flags
   --png100px
   -- - au.png
   -- - au.png
   --index.js
   --CountryFlagByCode.js

index.js

const context = require.context('./png100px', true, /.png$/);

const obj = {};
context.keys().forEach((key) => {
   const countryCode = key.split('./').pop() // remove the first 2 characters
      .substring(0, key.length - 6); // remove the file extension
   obj[countryCode] = context(key);
});

export default obj;

Suggestion : 5

I can load images dynamically from a folder in Nuxt (+ webpack) simply with a method like: Then I moved to Vite, and Require is not defined here (using rollup). How can I solve this, with nuxt / vite? Any idea? So I'd like to load all the SVG-s from the given folder somehow. But still have no idea how to convert the dynamically imported icon from img to svg. This is ridiculous... Same problem here, I can't find a way to dynamically import SVG icons. I might have an answer for you if you need to import components or images with a dynamic names:

1._
getServiceIcon(iconName) {
   return require('../../static/images/svg/services/' + iconName + '.svg');
}
2._
const useImage = ((url) => {
   return new URL(`/src/${url}`,
      import.meta.url).href;
});

Suggestion : 6

The public folder contains static files such as index.html, javascript library files, images, and other assets, etc. which you don’t want to be processed by webpack. Files in this folder are copied and pasted as they are directly into the build folder. Only files inside the `public` folder can be referenced from the HTML. For the above file structure, in order to display the src/assets/gfg.png in Intro component, it can be simply imported and shown like this:  Project Structure: It will look like the following. Now create an assets folder and put any sample image into it, like here we have kept the gfg.png file. Also, we have kept the gfgPublic.png file in the public folder.

Step 1: Create a React application using the following command:

npx create - react - app foldername

Step 2: After creating your project folder i.e. foldername, move to it using the following command:

cd foldername
3._
import Intro from './components/Intro';
import './App.css';
  
function App() {
  return (
    <div className="App">
      <Intro />
    </div>
  );
}
  
export default App;

For showing image inside the public folder, ‘public/gfgPublic.png’. The URL for the src attribute will be process.env.PUBLIC_URL + “/gfgPublic.png”. Here, the PUBLIC_URL will be replaced with the URL of the `public` folder during the build.

<img src={process.env.PUBLIC_URL + "/gfgPublic.png"} />

For showing image inside the public folder, ‘public/gfgPublic.png’. The URL for the src attribute will be process.env.PUBLIC_URL + “/gfgPublic.png”. Here, the PUBLIC_URL will be replaced with the URL of the `public` folder during the build.

import gfgLogo from "../assets/gfg.png";
  
const Intro = () => {
  return (
    <div className="App">
  
      Image at src/assets/gfg.png : <br />
      <img src={gfgLogo} />
      <br />
  
      Image at public/gfgPublic.png: <br />
      <img src={process.env.PUBLIC_URL + "/gfgPublic.png"} />
      <br />
  
    </div>
  );
};
  
export default Intro;

Suggestion : 7

I found some code here: https://stackoverflow.com/questions/42118296/dynamically-import-images-from-a-directory-using-webpack I want to import all the images in a folder but don’t want to import them individually with a sequence of lines like Keep in mind that Webpack will add any image found in that folder to the bundle. So if there a thousand images, all of them will be added.

Does anyone know how this can be done?

Here is my current (working) code in image.js:

import React from "react";
import { styled } from "frontity";
// import photo from "./assets/balloons.jpg"


function importImages(r) {
  // console.log(r)
  let images = {};
  r.keys().map((item) => { images[item.replace('./', '')] = r(item); });
  return images;
}
const images = importImages(require.context('./assets', false, /\.(png|jpe?g|svg)$/));

const ImageElement = ({pic = 'mountains'}) => {
  const filename = pic + '.jpg'
  return <Image src={images[filename]} />
}

export default ImageElement;

Suggestion : 8

This setup makes your code a lot more portable as everything that is closely coupled now lives together. Let's say you want to use /my-component in another project, copy or move it into the /components directory over there. As long as you've installed any external dependencies and your configuration has the same loaders defined, you should be good to go. In order to import a CSS file from within a JavaScript module, you need to install and add the style-loader and css-loader to your module configuration: However, let's say you're locked into your old ways or you have some assets that are shared between multiple components (views, templates, modules, etc.). It's still possible to store these assets in a base directory and even use aliasing to make them easier to import.

Let's make a minor change to our project before we get started:

dist/index.html

 <!DOCTYPE html>
 <html>

 <head>
    <meta charset="utf-8" />
    - <title>Getting Started</title>
    + <title>Asset Management</title>
 </head>

 <body>
    - <script src="main.js"></script>
    + <script src="bundle.js"></script>
 </body>

 </html>

webpack.config.js

 const path = require('path');

 module.exports = {
    entry: './src/index.js',
    output: {
       -filename: 'main.js',
       +filename: 'bundle.js',
       path: path.resolve(__dirname, 'dist'),
    },
 };
3._
npm install--save - dev style - loader css - loader

src/style.css

.hello {
   color: red;
}

src/index.js

 import _ from 'lodash'; +
 import './style.css';

 function component() {
    const element = document.createElement('div');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' '); +
    element.classList.add('hello');

    return element;
 }

 document.body.appendChild(component());