React Native Packager ships with some niceties for managing static images and the docs do a decent job of getting things started for small scale applications. However, as projects scale, nested hierarchies develop and refactoring becomes necessary to maintain a clean code base. When that time comes, having local assets required in via relative paths becomes a bit of a chore to keep up with.

Consider the following basic application structure for a simple menu component and its accompanying static image assets:

app  
   |--components
      |--img
         |--menu-icon@1x.ios.png
         |--menu-icon@1x.android.png
         |--profile-icon@1x.ios.png
         |--profile-icon@1x.android.png
         |--settings-icon@1x.ios.png
         |--settings-icon@1x.android.png
      |--menu.js
      |--menuStyles.js

As suggested in the React Native docs, static assets can be imported directly into the source attribute of the Image component. This is what an image component would look like when sourcing the correct image from within the menu.js file.

<Image source={require(‘./img/menu-icon.png’) />

Packager will simply follow the relative path into the img folder and select the image that is both appropriate for the platform (iOS or Android) and the correct pixel density for the device that’s running the app. This pattern will pass muster in the early stages of the project, but can be tedious to maintain as the application scales for a couple reasons.

First, as applications grow it’s often handy to organize components into subdirectories grouped either by feature or by type. As you begin to move components around and add nested structures to the app, the static assets now must travel along with its component since they are being served from a sister directory. Either that, or the relative path to the asset must be updated, but this second option adds a bit of randomness in the file structure. The primary aim of refactoring is to impose order, not chaos.

Additionally, if the need arises to reuse an asset for a second or third component one of two things becomes necessary. Either a duplicate of the asset must be maintained along with the new component, or again, a seemingly random relative path has to be maintained as a reference to an asset that lives in a completely different directory somewhere in the file structure. None of these are very appealing options.

Fortunately, there is a better way to manage static assets with something akin to an absolute path. Consider that same feature but this time with a separately maintained assets directory and a slightly more nested structure. It’s a slightly contrived example but you get the idea.

app  
   |--assets
      |--img
         |--menu-icon@1x.ios.png
         |--menu-icon@1x.android.png
         |--profile-icon@1x.ios.png
         |--profile-icon@1x.android.png
         |--settings-icon@1x.ios.png
         |--settings-icon@1x.android.png
      |--images.js
      |--package.json
   |--components
      |--nav
         |--menu.js

Inside the images.js file, we can simply maintain a dictionary object that houses a single reference to the asset path.

const images = {
	menuIcon: require(‘./img/menu-icon.png’),
	profileIcon: require(‘/img/profile-icon.png’),
	settingsIcon: require(‘./img/settings-icon.png’),
};


export default images;

All that’s needed now is to expose a reference to our image dictionary to the rest of the app. This is achieved by adding a package.json file to the folder that we wish to export. Inside the package.json we simply create a name for our new module. { “name”: “@assets” } Now, wherever we need to use an asset we can import the image dictionary as a module and simply access any key we desire as the image source.

Import Images from ‘@assets/images’;

<Image source={Images.menuIcon} />

The wins here for code maintenance are substantial. Since the only relative paths we are using are now within the dictionary, they are likely to never change. Even if they did, say in the case of adding nested hierarchies to the images folder, only a single update would need to be made in the dictionary and this change would ripple across wherever that asset is imported. As a bonus, the image components are slightly easier to read and removing all local image folders into an assets directory cleans up any component directories.

Happy refactoring!