How to Use React Native FS (RNFS): 2026 Guide

React Native FS, commonly called RNFS, gives a React Native app access to native file operations on Android and iOS. We can use it to create directories, write and read files, inspect folder contents, copy or delete files, and download remote files to the device.
For most app-owned files, the basic workflow is simple: install RNFS, choose an app-specific directory such as DocumentDirectoryPath, build a file path, and call methods such as writeFile() or readFile(). The examples below show that workflow first, followed by the platform rules that matter in current React Native apps.
React Native FS Quick Answer
npm install react-native-fs
cd ios && bundle exec pod install && cd ..import RNFS from 'react-native-fs';
const filePath = `${RNFS.DocumentDirectoryPath}/example.txt`;
await RNFS.writeFile(filePath, 'Hello from React Native', 'utf8');
const contents = await RNFS.readFile(filePath, 'utf8');
console.log(contents);DocumentDirectoryPath is private to the app, persists between launches, and normally requires no storage permission. Use CachesDirectoryPath for files that can be downloaded or generated again, and TemporaryDirectoryPath for short-lived files.
What Is React Native FS?
React Native FS is a native filesystem library with a JavaScript API. Unlike browser storage, it lets the app work with actual files and directories on the device.
| Method | Purpose |
writeFile() | Create or replace a file |
readFile() | Read a file as text or Base64 |
mkdir() | Create a directory |
readDir() | List files and subdirectories |
exists() | Check whether a path exists |
copyFile() | Copy a file |
moveFile() | Move or rename a file |
unlink() | Delete a file or directory |
downloadFile() | Download a remote file |
stat() | Read file metadata |
RNFS is useful for offline documents, exported reports, cached downloads, logs, generated images, and other files owned by the app. It does not bypass the Android or iOS storage security model.
Installing React Native FS
Install the original package with npm or Yarn:
npm install react-native-fsOr install it with Yarn:
yarn add react-native-fsModern React Native projects use autolinking, so we should not run the old react-native link command. On iOS, install the CocoaPods dependency and rebuild the app:
cd ios
bundle exec pod install
cd ..Then import the library:
import RNFS from 'react-native-fs';A Short Compatibility Note for Current React Native Apps
The original react-native-fs repository has not kept pace with New Architecture-only React Native releases. Existing projects can continue with a tested version, but new projects should verify compatibility with their exact React Native version.
One current option is the maintained @dr.pogodin/react-native-fs fork:
npm install @dr.pogodin/react-native-fsimport * as RNFS from '@dr.pogodin/react-native-fs';Its main file APIs closely follow RNFS, so the examples in this guide remain applicable. Expo projects can also consider expo-file-system. Whichever package we choose, a clean Android and iOS release build should be part of the compatibility check.
Understanding React Native FS Paths
RNFS exposes directory constants so we do not have to hard-code platform-specific paths.
| RNFS path | Platform | Best use |
DocumentDirectoryPath | Android and iOS | Durable, app-owned files |
CachesDirectoryPath | Android and iOS | Regenerable downloads and cached data |
TemporaryDirectoryPath | Android and iOS | Short-lived exports and intermediate files |
DownloadDirectoryPath | Android | Shared Downloads, subject to Android storage rules |
ExternalDirectoryPath | Android | App-specific files on external storage |
MainBundlePath | iOS | Read-only files packaged with the app |
For ordinary file operations, DocumentDirectoryPath is the safest default:
const notesDirectory = `${RNFS.DocumentDirectoryPath}/notes`;
const notePath = `${notesDirectory}/welcome.txt`;We should store a relative value such as notes/welcome.txt in a database, not the full iOS sandbox path. The container portion of an iOS path can change, so the current RNFS directory constant should be resolved at runtime.
Writing a File with React Native FS
writeFile() creates a file if it does not exist and replaces its contents if it does. Its arguments are the destination path, content, and encoding.
import RNFS from 'react-native-fs';
export async function saveNote(): Promise<string> {
const path = `${RNFS.DocumentDirectoryPath}/note.txt`;
await RNFS.writeFile(path, 'Our first local note', 'utf8');
return path;
}Common encodings include utf8 for text and base64 for binary data. Reading or writing a large file as one Base64 string can consume substantial memory, so we should reserve that approach for small, bounded files.
Let’s Build Your React Native App Together!
We build powerful React Native apps that run smoothly on iOS and Android — fast, reliable, and ready to scale.
To add content without replacing the file, use appendFile():
await RNFS.appendFile(filePath, '\nAnother line', 'utf8');Reading a File with React Native FS
Use readFile() with the same encoding used to write the file:
import RNFS from 'react-native-fs';
export async function readNote(): Promise<string> {
const path = `${RNFS.DocumentDirectoryPath}/note.txt`;
return RNFS.readFile(path, 'utf8');
}File operations can fail because a path is wrong, a file was removed, storage is full, or access has changed. Wrap user-facing operations in try...catch and preserve the native error code for diagnostics:
try {
const contents = await RNFS.readFile(filePath, 'utf8');
console.log(contents);
} catch (error) {
console.error('Unable to read file', error);
}Creating a Directory
Create a folder with mkdir() before writing files into it:
const reportsDirectory = `${RNFS.DocumentDirectoryPath}/reports`;
if (!(await RNFS.exists(reportsDirectory))) {
await RNFS.mkdir(reportsDirectory);
}If several operations may create the same directory simultaneously, an existence check is not a lock. Centralizing file mutations in one storage service helps avoid two tasks changing the same path at once.
Listing Files and Folders
readDir() returns directory entries with names, paths, sizes, timestamps, and helpers that identify files and directories.
const entries = await RNFS.readDir(RNFS.DocumentDirectoryPath);
const files = entries
.filter(entry => entry.isFile())
.map(entry => ({
name: entry.name,
path: entry.path,
size: entry.size,
}));For a folder containing thousands of files, avoid parsing every file merely to render a screen. A small database index is more efficient for search, sorting, and pagination.
Checking Whether a File Exists
const path = `${RNFS.DocumentDirectoryPath}/settings.json`;
if (await RNFS.exists(path)) {
const settings = await RNFS.readFile(path, 'utf8');
console.log(JSON.parse(settings));
}The file can still change after the check. For critical operations, attempt the read, move, or delete and handle its specific error rather than treating exists() as a guarantee.
Copying, Moving, and Renaming Files
Use copyFile() when both copies should remain:
await RNFS.copyFile(sourcePath, destinationPath);Use moveFile() to move a file or rename it within a directory:
const oldPath = `${RNFS.DocumentDirectoryPath}/draft.txt`;
const newPath = `${RNFS.DocumentDirectoryPath}/final.txt`;
await RNFS.moveFile(oldPath, newPath);Both operations run natively, which is more efficient than reading the bytes into JavaScript and writing them again.
Deleting Files and Directories
unlink() deletes a file. It can also remove a directory and its contents, so the target path must be validated carefully.
const path = `${RNFS.DocumentDirectoryPath}/old-report.pdf`;
if (await RNFS.exists(path)) {
await RNFS.unlink(path);
}Never join an unchecked filename or user-controlled relative path to a writable directory. Reject path separators and .. segments, and confirm that a cleanup target remains under the directory the app is allowed to manage.
Downloading a File with Progress
downloadFile() downloads directly to a local path and returns a job ID plus a promise. The job ID can be passed to stopDownload() if the user cancels the operation.
import RNFS from 'react-native-fs';
export function downloadReport(
onProgress: (percentage: number | null) => void,
) {
const destination = `${RNFS.CachesDirectoryPath}/report.pdf`;
const task = RNFS.downloadFile({
fromUrl: 'https://example.com/files/report.pdf',
toFile: destination,
progressDivider: 5,
progress: ({bytesWritten, contentLength}) => {
onProgress(
contentLength > 0 ? (bytesWritten / contentLength) * 100 : null,
);
},
});
const promise = task.promise.then(result => {
if (result.statusCode < 200 || result.statusCode >= 300) {
throw new Error(`Download failed with HTTP ${result.statusCode}`);
}
return destination;
});
return {
jobId: task.jobId,
promise,
cancel: () => RNFS.stopDownload(task.jobId),
};
}Some servers do not provide a usable content length, so progress may need an indeterminate state. For important downloads, write to a .partial path first, verify the response and expected checksum, and then move the completed file to its final name.
React Native FS Permissions on Android and iOS
RNFS does not require a general storage permission for every operation. The correct approach depends on who owns the file and where it should appear.
| Requirement | Recommended approach |
| Store a private app file | App-specific path; no broad storage permission |
| Cache a downloadable file | CachesDirectoryPath; no broad storage permission |
| Let an Android user choose a document | Storage Access Framework or a document picker |
| Export a PDF to a user-chosen Android location | A system Save As flow |
| Select photos or videos | The system photo picker or a maintained media picker |
| Publish media to the Android gallery | MediaStore through a compatible library or native module |
| Open or export a user-selected iOS document | A document picker or share sheet |
Android Storage Rules
App-specific internal and external directories normally need no storage permission. On current Android versions, requesting WRITE_EXTERNAL_STORAGE does not restore unrestricted shared-storage access, and it provides no additional access for apps targeting Android 11 or later.
Android pickers commonly return a content:// URI. It may represent a local file, cloud document, or media-provider item; it is not guaranteed to be a path RNFS can read directly. Use a URI-aware API or copy the selected item into app-owned storage while access is available.
DownloadDirectoryPath is only a path constant. It does not override scoped storage. When users expect a PDF or document in a public location, a Save As flow is more reliable than requesting broad storage access.
Let’s Build Your React Native App Together!
We build powerful React Native apps that run smoothly on iOS and Android — fast, reliable, and ready to scale.
iOS Storage Rules
iOS keeps each app inside its own sandbox. RNFS can freely manage files in the app’s container, but it cannot provide unrestricted access to files owned by other apps.
Use DocumentDirectoryPath for durable app-owned content, cache storage for reproducible files, and a document picker or share sheet when a user wants to open or export an external document. Cache and temporary files may be purged by the operating system.
Handling Large Files Efficiently
readFile(path, 'base64') loads the entire file into JavaScript and increases its encoded size. That can freeze the interface or exhaust memory when the file is a video, archive, or high-resolution image.
For large files:
- download, copy, or move them using native file operations;
- avoid Base64 unless another API specifically requires it;
- process bounded chunks when the algorithm supports chunking;
- use a native upload or background-transfer library for long-running transfers;
- test on a physical, lower-memory device using a release build.
React Native FS Best Practices
- Choose the directory by lifecycle: documents for durable data, cache for replaceable data, and temporary storage for short-lived work.
- Store relative file keys instead of permanent absolute iOS paths.
- Validate filenames before constructing a path from user or server input.
- Distinguish not-found, permission, low-storage, network, and cancellation failures.
- Avoid concurrent writes to the same destination; use unique temporary filenames.
- Validate download status, content type, size, and checksum when integrity matters.
- Keep passwords and tokens in Keychain- or Keystore-backed secure storage, not ordinary RNFS files.
- Delete stale cached and partial files according to an age or size policy.
Common React Native FS Errors
| Symptom | Likely cause | What to check |
ENOENT or “file not found” | Incorrect path, missing parent directory, or deleted file | Log the sanitized path and create the parent directory |
| Permission denied | Path outside app-owned storage or revoked access | Use an app-specific directory or the proper system picker |
| No space left on device | Destination volume is full | Remove safe cache files and let the user retry |
RNFS cannot read content://... | A provider URI was treated as a file path | Use a URI-aware API or copy the content locally |
| Downloaded file is unusable | HTTP error, partial transfer, or unexpected content | Check status, expected size, MIME type, and checksum |
| File disappears later | It was stored in cache or temporary storage | Move durable content to the documents directory |
When Not to Use RNFS
RNFS is designed for path-based file operations. It is not the best choice for structured data that needs queries and transactions, credentials requiring secure hardware-backed storage, arbitrary cloud-provider documents, or transfers that must continue reliably after the app is terminated.
In those cases, use a database, secure-storage library, document picker, MediaStore integration, or background-transfer service that matches the requirement.
Frequently Asked Questions
Does React Native FS work on both Android and iOS?
Yes. RNFS exposes a shared API for both platforms, although available directories and storage rules differ. Platform-specific constants should be guarded, and every supported device and OS version should be tested.
Does RNFS need storage permission on Android?
Not for app-specific directories such as the document and cache locations. Shared documents and media require purpose-specific system APIs, including document pickers, Save As flows, the photo picker, or MediaStore.
Where does React Native FS store files?
It stores files at the path we provide. DocumentDirectoryPath points to private, durable app storage, while cache and temporary directory constants point to locations whose contents the operating system may remove.
Can React Native FS read PDF and image files?
RNFS can read their bytes, copy them, move them, delete them, or download them. Rendering a PDF or decoding and displaying an image still requires an appropriate viewer or media component.
Can RNFS access files outside the app?
Only when the platform grants appropriate access. iOS sandboxing and Android scoped storage prevent arbitrary browsing. System pickers are the preferred way to let users select or export files outside app-owned storage.
Is React Native FS suitable for large files?
Native copy, move, and download operations can handle large files. Loading an entire binary into JavaScript as Base64 is memory-intensive, so large transfers should stay native and support progress and cancellation.
Conclusion
React Native FS makes app-owned file handling straightforward: select the correct directory, build a path, and use methods such as writeFile(), readFile(), readDir(), moveFile(), unlink(), and downloadFile().
The most important platform rule is equally simple. Private app directories normally need no broad storage permission; user-owned documents and shared media should go through the system APIs designed for them. With validated paths, deliberate storage locations, and careful handling of downloads and large files, RNFS provides a practical filesystem layer for React Native apps.



