Dynamically import images from a directory using webpack
ReactjsWebpackEcmascript 6Reactjs Problem Overview
So here's my current workflow for importing images and icons in webpack via ES6:
import cat from './images/cat1.jpg'
import cat2 from './images/cat2.svg'
import doggy from './images/doggy.png'
import turtle from './images/turtle.png'
<img src={doggy} />
This gets messy quick. Here's what I want:
import * from './images'
<img src={doggy} />
<img src={turtle} />
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.
Anyone seen this done, or have any thoughts on the best way to go about it?
UPDATE:
Using the selected answer, I was able to do this:
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']} />
Reactjs Solutions
Solution 1 - Reactjs
> 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.
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)$/));
Solution 2 - Reactjs
A functional approach to solve this problem:
const importAll = require =>
require.keys().reduce((acc, next) => {
acc[next.replace("./", "")] = require(next);
return acc;
}, {});
const images = importAll(
require.context("./image", false, /\.(png|jpe?g|svg)$/)
);
Solution 3 - Reactjs
It's easy. You can use require
(a static method, import is just for dynamic files) inside the render
. Like the example below:
render() {
const {
someProp,
} = this.props
const graphImage = require('./graph-' + anyVariable + '.png')
const tableImage = require('./table-' + anyVariable2 + '.png')
return (
<img src={graphImage}/>
)
}
Solution 4 - Reactjs
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;
I read a file like this:
CountryFlagByCode.js
import React from 'react';
import countryFlags from './index';
const CountryFlagByCode = (countryCode) => {
return (
<div>
<img src={countryFlags[countryCode.toLowerCase()]} alt="country_flag" />
</div>
);
};
export default CountryFlagByCode;
Solution 5 - Reactjs
UPDATE It seems like I didnt quite understand the question. @Felix got it right so check his answer. The following code will work in a Nodejs environment only.
Add an index.js
file in the images
folder
const testFolder = './';
const fs = require('fs');
const path = require('path')
const allowedExts = [
'.png' // add any extensions you need
]
const modules = {};
const files = fs.readdirSync(testFolder);
if (files && files.length) {
files
.filter(file => allowedExts.indexOf(path.extname(file)) > -1)
.forEach(file => exports[path.basename(file, path.extname(file))] = require(`./${file}`));
}
module.exports = modules;
This will allow you to import everything from another file and Wepback will parse it and load the required files.
Solution 6 - Reactjs
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.
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;
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.
// 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>
)
})}
</>
);