Static assets in Expo for React Native
I was confused by Expo’s instructions to use expo-asset for embed my static asset files
It refers to a static asset as something that is bundled with your app’s native binary, but then goes on in the same paragraph to say they can be served locally from your project or remotely over the network.
So is an asset that is served remotely still considered a static asset in this context? I’m confused.
Nevertheless, they explain how it works a little further down:
Locally stored assets are served over HTTP in development. They are automatically bundled into your app binary at the build time for production apps and served from disk on a device.
However, it says they are automatically bundled into your app binary at build time, but right after goes on to say that you can use expo-asset to do that.
So is expo-asset a requirement? or is it automatic? Still a little confusing.
Moving on, I attempted to work with it but struggled to know exactly what path I should be using. Another page that I found explained this:
The paths should be relative to the project root so that the file names, whether specified directly or using a directory, will become the resource names.
What does them being relative to the project directory have to do with the the filename becoming the resource name? The phrasing is a little confusing.
I’m a bit susceptible to poor phrasing that misleads a sentence. In this case, what I find myself wanting explained is “The file names should be unique across directories as they will become the unique resource names”. It seems like that’s what the original sentence is trying to make clear (That or something similar), but I’m left not sure if that’s what it’s getting at or if that’s even correct.
I eventually continued on ignoring the docs and trying paths and combinations of approaches until it worked.
What I did…
I added the needed files to my app.config.js:
(I found that the @ alias didn’t work in this file though)
"plugins": [
...
[
"expo-asset",
{
"assets": [
"./assets/audio/error.mp3",
"./assets/audio/recording_start.mp3",
"./assets/audio/recording_stop.mp3",
"./assets/audio/processing.mp3"
]
},
]
...
],
I then created a function that loaded all the assets I needed to use. When the app first starts, I run this function.
export async function preloadAudioAssets() {
try {
await Promise.all([
Asset.loadAsync(require("@/assets/audio/recording_start.mp3")),
Asset.loadAsync(require("@/assets/audio/recording_stop.mp3")),
Asset.loadAsync(require("@/assets/audio/processing.mp3")),
Asset.loadAsync(require("@/assets/audio/error.mp3"))
]);
console.log('Audio assets preloaded successfully');
} catch (error) {
console.error('Error preloading audio assets:', error);
}
}
Then, when I want to run one of the sounds, I pass it to my custom playImmediately function (that takes a URI), like this.
export async function playRecordingStartSound(): Promise<void> {
try {
await playImmediately( require('@/assets/audio/recording_start.mp3'), 'Listening' );
} catch (error) {
console.error('Error playing recording start sound:', error);
}
}
At this point I still don’t quite know if this is the best approach, but it worked for me on Android.
Note: I’m pretty sure this won’t work unless you rebuild your project. app.config.js changes often affect the build process (likely bundling those binary assets), so I don’t think you can use the sounds after simply an OTA (Over the Air update), as they won’t be sent over.
I don’t think I tested this, but that would be my guess from the docs.
I could be wrong though, drop a comment if you want to confirm for others.
Thanks…
I also dissect and speculate on design and development.
Digging into subtle details and implications, and exploring broad perspectives and potential paradigm shifts.
Check out my conceptual articles on Substack or find my latest below.
You can also find me on Threads, Bluesky, Mastodon, or xTwitter for more diverse posts about ongoing projects.
Leave a Reply