This repository demonstrates and validates the documentation updates proposed in Expo PR #41272.
The guide below explains how to clone this example repo, run it on Android/iOS, and verify both methods of accessing files:
- Using
expo-asset(Recommended) - Using
Paths.bundle(Advanced / Native-only)
git clone https://github.com/dileepapeiris/expo-file-system-bundled-pr-verification.git
cd expo-file-system-bundled-pr-verification
npm installBefore adding custom assets to the native bundles, we must generate the native folders (android and ios).
npx expo prebuildNote: We run this before the setup below. If you run
prebuildagain later, it may overwrite the manual changes made in the next steps (specifically the iOS Xcode configuration).
The Paths.bundle example will NOT work unless example.txt is manually added into the native bundle. Expo does not automatically copy JS project files into the native bundle structure.
Goal: Place the file in android/app/src/main/assets/.
-
If the assets folder does not exist, create it:
mkdir -p android/app/src/main/assets
-
Copy the example file:
cp exampleFile/example.txt android/app/src/main/assets/
Goal: Add the file to the "Copy Bundle Resources" build phase in Xcode.
-
Open the iOS project workspace:
xed ios
-
In Xcode:
- Go to the Project Navigator (left sidebar).
- Select YourApp (the root node).
- Select the Target (YourApp).
- Go to the Build Phases tab.
- Expand Copy Bundle Resources.
- Drag
example.txt(from Finder) into this list. - Ensure "Copy items if needed" is checked if prompted.
This ensures the file becomes part of the raw iOS Main Bundle.
Now that the native files are in place, run the app.
npx expo run:androidnpx expo run:iosThe app contains two tabs (or buttons) corresponding to the two methods below.
This works automatically with files inside your JavaScript project structure. No native file placement is needed.
Code Example:
import { Asset } from 'expo-asset';
import { File } from 'expo-file-system';
const doSomethingWithAsset = async () => {
// 1. Resolve asset from JS
const asset = Asset.fromModule(require('./assets/images/icon.png'));
// 2. Ensure downloaded
await asset.downloadAsync();
// 3. Read
const file = new File(asset.localUri!);
const content = await file.text();
console.log(content);
};Expected Behavior:
- Android: Loads normally.
- iOS: Loads normally.
- Source: Uses the JS-included assets folder.
This allows access to files explicitly added to the platform's native bundle (the steps you performed in Section 3).
Code Example:
import { File, Paths } from 'expo-file-system';
import { Platform } from 'react-native';
const readNativeBundle = async () => {
const fileName = 'example.txt';
// 1. Point to native bundle location
const bundleFile = new File(Paths.bundleDirectory, fileName);
if (!bundleFile.exists) {
console.warn('File not found in native bundle');
return;
}
let fileToRead = bundleFile;
// 2. iOS Specific Handling (Bundle is Read-Only)
if (Platform.OS === 'ios') {
const documentFile = new File(Paths.document, fileName);
// Must copy to document directory before reading
await bundleFile.copy(documentFile);
fileToRead = documentFile;
}
const content = await fileToRead.text();
console.log(content);
};Expected Behavior:
- Android: Can read directly from
android/app/src/main/assets/.bundleDirectoryresolves correctly. - iOS:
Paths.bundleDirectorypoints to the Main Bundle (read-only). The app logs a copy operation toPaths.document, then reads the file successfully.
The following table demonstrates the expected output for both methods on both platforms.
| Feature | Android | iOS |
|---|---|---|
| Normal Assets (expo-asset) |
Screen.Recording.2025-11-29.at.18.58.39.mov |
Simulator.Screen.Recording.-.iPhone.16.Plus.-.2025-11-29.at.19.03.35.mov |
| Bundle Path (Paths.bundle) |
Screen.Recording.2025-11-29.at.18.59.48.mov |
Simulator.Screen.Recording.-.iPhone.16.Plus.-.2025-11-29.at.19.04.50.mov |
If the documentation and implementation are correct, you should see:
-
expo-assetTab: Content loads correctly usingAsset.fromModule(require(...)). -
Paths.bundleTab (Android): Reads file directly from native assets. -
Paths.bundleTab (iOS): Successfully copies the file from the bundle to documents, then reads it.
This setup validates:
- The difference between JS-based assets (
expo-asset) and native bundle assets. - Why
bundleDirectorydoes not point to standard JS assets. - How to properly use
Paths.bundlewhen working with files inside the native project structure.