80

I know that to use a static image in react native you need to do a require to that image specifically, but I am trying to load a random image based on a number. For example I have 100 images called img1.png - img100.png in my directory. I am trying to figure out a way to do the following

<Image source={require(`./img${Math.floor(Math.random() * 100)}.png`)}/>

I know this intentionally does not work, but any workarounds would be greatly appreciated.

bgrober
  • 816
  • 1
  • 6
  • 4
  • Have you tried implementing this? I am unable to understand why this wouldn't work, as long as your file path to the image exists. Since RN0.14, you can directly require images from image paths, so this method should work – Nishanth Shankar Nov 25 '15 at 11:10
  • Did you find any solution to your problem? – Rahul Nov 09 '16 at 07:09

5 Answers5

126

For anyone getting to know the react-native beast, this should help :)

I visited a couple of sites in the past too, but found it increasingly frustrating. Until I read this site here.

It's a different approach but it eventually does pay off in the end. Basically, the best approach would be to load all your resources in one place. Consider the following structure

app  
   |--img
      |--image1.jpg
      |--image2.jpg
      |--profile
          |--profile.png
          |--comments.png
      |--index.js

In index.js, you can do this:

const images = {
    profile: {
        profile: require('./profile/profile.png'),
        comments: require('./profile/comments.png'),
    },
    image1: require('./image1.jpg'),
    image2: require('./image2.jpg'),
};

export default images;

In your views, you have to import the images component like this:

import Images from './img/index';

render() {
    <Image source={Images.profile.comments} />
}

Everybody has different means to an end, just pick the one that suits you best.

Da Man - Q: How is this answer using a variable?

Well, since require only accepts a literal string, you can't use variables, concatenated strings, etc. This is the next best thing. Yes, it still is a lot of work, but now you can do something resembling the OP's question:

render() {
  var images = { test100: "image100" };
  return (
    <View>
      <Text>
       test {images["test" + "100"]}
      </Text>
    </View>
  );
}
DerpyNerd
  • 4,743
  • 7
  • 41
  • 92
  • 1
    Thank you SO much! – Norfeldt Apr 20 '18 at 15:01
  • 1
    Just curious, does this mean every image is loaded into memory when the user hits the view and does this happen every time? – Aaron Nov 17 '18 at 16:34
  • 1
    @Aaron No, there's a difference between loading and rendering. These images are only rendered when you use them (something called lazy loading). See this as a bag of hooks. the actual rendering happens here: `` – DerpyNerd Nov 21 '18 at 16:16
  • Thank you. I used this implementation and I really like it. – Aaron Nov 21 '18 at 23:30
  • If only we had a more native solution like Android has :/ – DerpyNerd Nov 23 '18 at 14:07
  • 1
    How does this method use variable ? You are still specifying which image to load. – Da Man Jan 02 '19 at 08:28
  • @DerpyNerd Is there any way to provide the key like this: first image, with a space in between the name? – program_bumble_bee Jul 31 '19 at 07:21
  • 1
    @bubble-cord Are you talking about keys like "test 001"? In that case; yes. `{"test 001": ""}` is a valid js object and so is getting the keys' value using `images["test 001"]` – DerpyNerd Jul 31 '19 at 12:49
57

In JS require statements are resolved at bundle time (when the JS bundle is calculated). Therefore it's not supported to put variable expression as an argument for require.

In case of requiring resources it's even more trickier. When you have require('./someimage.png'), React Native packager will locale required image and it will be then bundled together with the app so that it can be used as a "static" resource when your app is running (in fact in dev mode it won't bundle the image with your app but instead the image will be served from the server, but this doesn't matter in your case).

If you want to use random image as a static resource you'd need to tell your app to bundle that image. You can do it in a few ways:

1) Add it as a static asset of your app, then reference to it with <Image src={{uri:'name_of_the_image_in_assets.png'}}/> (here is how you can add it to the native iOS app)

2) Require all the images upfront statically. Sth in a form of:

var randomImages = [
    require('./image1.png'),
    require('./image2.png'),
    require('./image3.png'),
    ...
];

Then in your code you can do:

<Image src={randomImages[Math.floor(Math.random()*randomImages.length)]}/>

3) Use network image with <Image src={{uri:'http://i.imgur.com/random.jpg'}}/>

kzzzf
  • 2,614
  • 1
  • 16
  • 7
  • 4
    what if you had a thousand images named image1.png, image2.png, image3.png etc - it's seems really odd that require won't accept a variable path and that you can't write a loop to create that randomImages array. Wouldn't one to be forced to write a thousand lines?! If I try to pass a variable into the require line, it complain the module is not found. – headwinds Jul 31 '16 at 14:43
  • what to do if list is coming from api ? – Anuj Sep 14 '19 at 13:28
  • This solved my issue for me. I was trying to dynamically create the links in a function and return that require() to the image source and was having continual errors. Great work! – IronWorkshop Oct 18 '19 at 23:09
7
class ImageContainer extends Component {
   this.state ={
     image:require('default-img')
   }
    <View>
           <Image source={this.state.image} />
    </View>
}

In the context of this discussion,I had this case where wanted to dynamically assign images for a particular background. Here I change state like this

this.setState({
  image:require('new-image')
})
Arausi Daniel
  • 191
  • 2
  • 9
  • This is a good start. Can you add how it solves the problem? A little description of what your code does would be nice. – harvpan Aug 03 '18 at 17:31
  • Inside require, if I have to use the dynamic image then how to do it ? ex: image:require(this.index+".png") where index = 1 initially. – Vasanth Apr 18 '20 at 07:46
  • The text in the require can be exchanged for state or props, take note that the purpose for the example above was for retrieving local images, for external images i.e "http://image.png" the syntax structure would change to – Arausi Daniel Apr 19 '20 at 13:11
3

I came to this thread looking for a way to add images in a dynamic way. I quickly found that passing in a variable to the Image -> require() was not working.

Thanks to DerpyNerd for getting me on the correct path.

After implementing the resources in one place I then found it easy to add the Images. But, I still needed a way to dynamically assign these images based on changing state in my application.

I created a function that would accept a string from a state value and would then return the Image that matched that string logically.

Setup

Image structure:

app  
  |--src
    |--assets
      |--images
        |--logos
          |--small_kl_logo.png
          |--small_a1_logo.png
          |--small_kc_logo.png
          |--small_nv_logo.png
          |--small_other_logo.png

        |--index.js
    |--SearchableList.js

In index.js, I have this:

const images = {
  logos: {
    kl: require('./logos/small_kl_logo.png'),
    a1: require('./logos/small_a1_logo.png'),
    kc: require('./logos/small_kc_logo.png'),
    nv: require('./logos/small_nv_logo.png'),
    other: require('./logos/small_other_logo.png'),
  }
};

export default images;

In my SearchableList.js component, I then imported the Images component like this:

import Images from './assets/images';

I then created a new function imageSelect in my component:

imageSelect = network => {
  if (network === null) {
    return Images.logos.other;
  }

  const networkArray = {
    'KL': Images.logos.kl,
    'A1': Images.logos.a1,
    'KC': Images.logos.kc,
    'NV': Images.logos.nv,
    'Other': Images.logos.other,
  };

  return networkArray[network];
};

Then in my components render function I call this new imageSelect function to dynamically assign the desired Image based on the value in the this.state.network:

render() {
  <Image source={this.imageSelect(this.state.network)} />
}

Once again, thanks to DerpyNerd for getting me on the correct path. I hope this answer helps others. :)

Ryan S
  • 427
  • 4
  • 4
  • 4
    I think you are overcomplicating things here. There is no need for the `imageSelect` function. Because `images` is a simple javascript object, you can dynamically call the keys in that object. e.g. ``. Just make sure they keys match with the possible network states :) – DerpyNerd Nov 15 '18 at 22:07
2

Here is a simple and truly dynamic solution(no renaming or import required) to the problem if you have a bigger no of files.

[Won't work for Expo Managed]

Although the question is old I think this is the simpler solution and might be helpful. But I beg a pardon for any terminological mistakes, correct me please if I do any.

INSTEAD OF USING REQUIRE WE CAN USE THE URI WITH NATIVE APP ASSETS FOR ANDROID (AND/OR iOS). HERE WE WILL DISCUSS ABOUT ANDROID ONLY

URI can easily be manipulated as per the requirement but normally it's used for network/remote assets only but works for local and native assets too. Whereas require can not be used for dynamic file names and dirs

STEPS

  1. Open android/app/src/main/assets folder from your App.js or index.js containing directory, if the assets folder doesn't exist create one.
  2. Make a folder named images or any NAME of your choice inside assets, and paste all the images there.
  3. Create a file named react-native.config.js in the main app folder containing App.js or index.js.
  4. Add these lines to the new js file:

module.exports = {
  project: {
    ios: {},
    android: {},
  },
  assets: ['./assets/YOUR_FOLDER_NAME/'],
};

at the place of YOUR_FOLDER_NAME use the newly created folder's name images or any given NAME

  1. Now run npx react-native link in your terminal from main app folder, this will link/add the assets folder in the android bundle. Then rebuild the debug app.
  2. From now on you can access all the files from inside android/app/src/main/assets in your react-native app. For example:
<Image
   style={styles.ImageStyle}
   source={{ uri: 'asset:/YOUR_FOLDER_NAME/img' + Math.floor(Math.random() * 100) + '.png' }}
/>
piedpipr
  • 155
  • 2
  • 7