DEV Community

Emilio Srougo
Emilio Srougo

Posted on

Dynamic imports in React-Native

Have you ever tried to do this in a React-Native app?

<Image source={require("./images/" + this.props.image)} />

If you have, you probably discovered that all the imports are statically analyzed at compile-time, and have to be a simple string, not a dynamic expression.
Nevertheless, in a recent project this was very frustrating; My requirements were to include a folder of images in the app bundle, and render one of them according to a response from the server.
What we can do, is to require all the images in a file:

// assets/images/index.js
const images = {
  dog: require("./dog.png"),
  cat: require("./cat.png")
}
export default images;

Then, we can use it like this:

// app/imageView.js
import images from "../assets/images"
const View = (props) => 
  <Image source={images[this.props.image]} />

The problem is that my images folder had a lot of images I couldn’t possibly manually require them all.
So, I used the power of automation and created a simple script to make the assets/images/index.js file for me:

// prepareImages.js
const fs = require("fs");
const files = fs.readdirSync("./assets/images").filter(x => x.includes("png"));
const ex =
  "{\n" +
  files.map(x => `"${x.split(".png")[0]}": require("./${x}"),`).join("\n") +
  "}";
const res = "export default " + ex;
fs.writeFileSync("./assets/images/index.js", res);

This will produce a nice file exporting every image in a folder.
Thank you for reading!

P.S: I found a babel plugin that does something like this: https://github.com/dushaobindoudou/babel-plugin-require-all but the documentation isn’t very clear (is in Chinese or something) so I tested it, and here is how it works:
given: const imges = requireAll('./assets/imgs')
it will output:

const $assets_images_cat = require('./assets/images/cat.png');

const $assets_images_dog = require('./assets/images/dog.png');

const images = {
  $assets_images_cat: $assets_images_cat,
  $assets_images_dog: $assets_images_dog
};

Top comments (7)

Collapse
 
thibmaek profile image
Thibault Maekelbergh

Nice! I modified it a little bit to take advantage of full ESM and non dynamic CJS requires:

Output:

...
export { default as imageAlert } from './alert.png';
...

Script:

const fs = require("fs");

const files = fs.readdirSync("./src/common/assets/images")
  .filter(x => x.includes("png"))
  .filter(x => !x.includes('@2x'))
  .filter(x => !x.includes('@3x'));

const res = files.map(file =>`export { default as image${file.split('.png')[0]} } from './${file}';`)
  .join('\n');

fs.writeFileSync("./src/common/assets/images/index.js", res);

Collapse
 
nuevoleonkx profile image
nuevoleonkx

Can you please provide an example? I want to use with an <Image> element with dynamic source inside a <FlatList>, something like this..

...
import * as ImageLibrary from '../../static/assets/icons';
...
<FlatList
        data={options}
        renderItem={({ item }) => (
          <TouchableHighlight>
            <Image
              source={`ImageLibrary.image${item.icon.split('.').slice(0, -1).join('.')}`}
            />
          </TouchableHighlight>
        )}
      />
...

Is it possible?

Thanks in advance

Collapse
 
thibmaek profile image
Thibault Maekelbergh

Seems like a small syntax error:

import * as ImageLibrary from '../../static/assets/icons';

<FlatList
  renderItem={({ item }) => <Image source={ImageLibrary[`image${item.icon.split('.').slice(0, -1).join('.')}`]} />}
/>

In an example this translates to:

// assets/icons.js
export { default as imageEditIcon } from './edit.png';

// index.js

// With item.icon = editIcon

<Image source={ImageLibrary.imageEditIcon}/>
...

You are using something called a computed key for an object, by taking advantage of template strings to interpolate the value into the string. With interpolation and writing it statically it would be the same as ImageLibrary['imageEditIcon'] which retrieves the value for ImageLibrary.imageEditIcon

Thread Thread
 
nuevoleonkx profile image
nuevoleonkx

i'm really sorry... it was my fault.. that's exactly what i'm trying to do. Thanks a lot!

Collapse
 
woutvannestestudent profile image
Wout Vanneste

Hi, I'm trying to do this, but I get the error that 'fs' is not defined. What package did you install to use 'require("fs")'? Because I'm unable to use the Node.js fs in my react-native application.

Collapse
 
rseromenho profile image
Ricardo Seromenho

Thanks. Great post!

Collapse
 
skptricks profile image
skptricks

Thanks for this post,Check out similar post : skptricks.com/2018/07/get-image-fr...