How to obtain a URI for an image asset in React Native (With Expo)
Tags: javascript, reactnative, expo
Published: 2020-05-22 22:13:58


Another way of importing and handling image assets without using `require()`, no third-party plugins required.

> I’m currently trying to master React (Native) so this is probably a horrible hack. I’ll be updating this post as I find better ways of doing this.

One of the issues I’ve found while trying to build the proof of concept for an app is that you need to "pre-populate" it with some dummy data just to test stuff out quickly.

It’s all good and well for strings, numbers, objects, etc. But then if you need to display images it gets a bit tricky.

Assuming you don’t want to use image from an external source, the quickest way to have pre-existing images when testing in a simulator or a physical device is to load them in your project’s `assets/` directory (or at least that’s the best solution I’ve found).

# The problem

After reading Expo’s documentation it initially seemed like the only way to "import" an image file in your code was by using require(). Now, this being 2020 I thought, surely, there must be a better way.

> Full disclosure: When I try to use require() in my project I get a compiler error saying "require is not defined". I’m not sure if I’ve caused it by migrating my project to Typescript.

> Apparently it’s a Metro/Webpack config issue but I haven’t been able to figure out how to fix it. If you, reader, happen to know how to fix it please drop me a message, thanks!

> Now let’s keep going.

# The other way

Turns out there’s another way, maybe writing a one more line of code, but using purely ESModules, the future!

First you need to import your image like:

```js
import exampleImage from './assets/images/example.png'
```

If you were to `console.log(exampleImage)` you’d get a number printed, like `1`, or `8`. I assume this is just an id or mapped value, however you prefer to call it, generated by Metro’s asset loader.

***

Now, the important bit:

React Native’s official Image Component provides us with a method called [resolveAssetSource()](https://reactnative.dev/docs/image#resolveassetsource). This method takes a "number" (related to what I’ve mentioned above) or an `ImageSource` object as its only parameter and returns an object with `width`, `height`, `scale` and `uri` properties, this last one being the one we care about in this case. This will look like:

```js
const exampleImageUri = Image.resolveAssetSource(exampleImage).uri
```

Now, what’s the point of all this you ask. Well, to be honest you could always use `require()` and it would work and that’s it right? Well not exactly.

## The problem with using require() for image assets

Using `require()` has a couple of caveats. First, it’s CommonJS and we’re working on the browser-side of things, where it doesn’t really belong, in the end you’re using just a synthetic `require()` being provided by the bundler.

Second, we’d have a mixture of ESModule `import`s and CommonJS `require()` in our codebase. Something we can easily avoid it, at least in this case.

Third, the second to most important in my opinion. The syntax is different when you want to display your image in an `<Image>` component.

### require (CommonJS)

```js
import { Image } from 'react-native';

const exampleImage = require('./assets/images/example.png')

<Image source={exampleImage} />
```

### import (ESModule)

```js
import { Image } from 'react-native';

import exampleImage from './assets/images/example.png'
const exampleImageUri = Image.resolveAssetSource(exampleImage).uri

<Image source={{uri: exampleImageUri}} />
```

### Bonus (Dynamic imports)

I know it’s also possible for `require()`, but you could even use dynamic imports, like:

```js
import { Image } from 'react-native';

const {default: exampleImage } = await import('./assets/images/example.png')
const exampleImageUri = Image.resolveAssetSource(exampleImage).uri
```

And lastly, by using import you’ll provide the source attribute to the `<Image/>` component in the same format you’ll need to provide it when you eventually finish your POC and start working with real data, fetching images from external URLs like CDNs and the like, that source format is and object with a `uri` property, like:

```js
{uri: exampleImageUri}
```

## Conclusion

In conclusion, this way you have a consistent way of handling image assets. All the code and components you wrote to handle and display your images when getting started building your app will still be useful once you switch from dummy to real data from external sources. No need to refactor components and update source props and the like.

Anyways, I hope this is useful to you, I’m still trying to master React (Native), this post is one part note-taking for myself and one part writing it down so somebody having the same issues can find it. If something I’ve stated in this post is wrong feel free to leave a note and I’ll try to get it corrected.

## A couple of things to know about the URI structure

This uri property is comprised of a normal looking URL, in the form of :

```
http://127.0.0.1:19001/assets/assets/images/examples.png?platform=<android|ios...>&hash=<asset_hash>?platform=<android|ios...>&dev=true&minify=false&hot=false
```

### A couple of things worth noting:

There are two assets/ directories in the path, I’m assuming the top one relates to an internal directory for the bundler and the second one being part of the string path we’ve specified in our import statement example.

For some reason it also has 2 sets of query strings. I’m assuming the second set being for the dev server and the first one some other internal Expo-related service.

Now I’m not sure how this URI translates in production but, according to the [Assets Guide](https://docs.expo.io/guides/assets/) in Expo’s official documentation, Expo uploads the project’s assets to Amazon CloudFront and I’m assuming creates some kind of map/replaces all assets references with the CDN URLs internally.

## References
- [Assets — Expo Documentation](https://docs.expo.io/guides/assets/)
- [Asset SDK — Expo Documentation](https://docs.expo.io/versions/v37.0.0/sdk/asset/)
- [Image — Core Components — React Native](https://reactnative.dev/docs/image)
- [import — Javascript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)

Originally posted in:
{% medium https://medium.com/@fdefreitas/how-to-obtain-a-uri-for-an-image-asset-in-react-native-with-expo-88dfbe1023b8 %}