This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

Expo FileSystem iconExpo FileSystem

A library that provides access to the local file system on the device.

Android
iOS
tvOS
Included in Expo Go

expo-file-system provides access to files and directories stored on a device or bundled as assets into the native project. It also allows downloading files from the network.

Installation

Terminal
- npx expo install expo-file-system
- yarn expo install expo-file-system
- pnpm expo install expo-file-system
- bun expo install expo-file-system

If you are installing this in an existing React Native app, make sure to install expo in your project.

Configuration in app config

You can configure expo-file-system using its built-in config plugin if you use config plugins in your project (Continuous Native Generation (CNG)). The plugin allows you to configure various properties that cannot be set at runtime and require building a new app binary to take effect. If your app does not use CNG, then you'll need to manually configure the library.

Example app.json with config plugin

app.json
{ "expo": { "plugins": [ [ "expo-file-system", { "supportsOpeningDocumentsInPlace": true, "enableFileSharing": true } ] ] } }

Configurable properties

NameDefaultDescription
supportsOpeningDocumentsInPlacefalse
Only for: 
iOS

A boolean to enable LSSupportsOpeningDocumentsInPlace in Info.plist. This allows the app to open documents in place.

enableFileSharingfalse
Only for: 
iOS

A boolean to enable UIFileSharingEnabled in Info.plist. This enables file sharing in the iOS Files app, making the app's Documents directory accessible to users through the Files app, iTunes File Sharing, and other file management tools.

Are you using this library in an existing React Native app?

If you're not using Continuous Native Generation (CNG) or you're using native ios project manually, then you need to add the LSSupportsOpeningDocumentsInPlace and UIFileSharingEnabled keys to your project's ios/[app]/Info.plist:

<key>LSSupportsOpeningDocumentsInPlace</key> <true/> <key>UIFileSharingEnabled</key> <true/>

Usage

import { File, Directory, Paths } from 'expo-file-system';

The File and Directory instances hold a reference to a file, content, or asset URI.

The file or directory does not need to exist — an error will be thrown from the constructor only if the wrong class is used to represent an existing path (so if you try to create a File instance passing a path to an already existing directory).

Features

  • Both synchronous and asynchronous, read and write access to file contents
  • Creation, modification and deletion
  • Available properties, such as type, size, creationDate, and more
  • Ability to read and write files as streams or using the FileHandle class
  • Easy file download/upload using downloadFileAsync or expo/fetch
  • File previews using platform-native flows

Examples

Writing and reading text files
example.ts
import { File, Paths } from 'expo-file-system'; try { const file = new File(Paths.cache, 'example.txt'); file.create(); // can throw an error if the file already exists or no permission to create it await file.write('Hello, world!'); // or `file.writeSync('Hello, world!');` for synchronous call console.log(file.textSync()); // Hello, world! } catch (error) { console.error(error); }
Picking files using system pickers

Usage with expo-document-picker:

example.ts
import { File } from 'expo-file-system'; import * as DocumentPicker from 'expo-document-picker'; try { const result = await DocumentPicker.getDocumentAsync({ copyToCacheDirectory: true }); if (!result.canceled) { const { uri } = result.assets[0]; const file = new File(uri); console.log(file.textSync()); } } catch (error) { console.error(error); }

Using the built-in pickFileAsync or pickDirectoryAsync method on Android:

example.ts
import { File } from 'expo-file-system'; try { const file = new File.pickFileAsync(); console.log(file.textSync()); } catch (error) { console.error(error); }
Downloading files

Using downloadFileAsync:

example.ts
import { Directory, File, Paths } from 'expo-file-system'; const url = 'https://pdfobject.com/pdf/sample.pdf'; const destination = new Directory(Paths.cache, 'pdfs'); try { destination.create(); const output = await File.downloadFileAsync(url, destination); console.log(output.exists); // true console.log(output.uri); // path to the downloaded file, e.g., '${cacheDirectory}/pdfs/sample.pdf' } catch (error) { console.error(error); }

Or using expo/fetch:

example.ts
import { fetch } from 'expo/fetch'; import { File, Paths } from 'expo-file-system'; const url = 'https://pdfobject.com/pdf/sample.pdf'; const response = await fetch(url); const src = new File(Paths.cache, 'file.pdf'); await src.write(await response.bytes());
Previewing files

Use File.preview() to open local files in the native viewer. Android opens an installed app that supports the file's MIME type. iOS uses Quick Look.

File.preview() and File.canPreview() accept a File or an ordered File[]. For one file, you can also call file.preview() and file.canPreview() directly.

example.ts
import { File, Paths } from 'expo-file-system'; const file = await File.downloadFileAsync( 'https://pdfobject.com/pdf/sample.pdf', new File(Paths.cache, 'sample.pdf') ); if (await File.canPreview(file)) { await File.preview(file, { title: 'Sample PDF' }); }

Multiple files are supported on iOS only. This example opens the notes first and lets the user navigate to the PDF:

example.ts
const notes = new File(Paths.cache, 'notes.txt'); await notes.write('Notes about the PDF'); const files = [file, notes]; if (await File.canPreview(files)) { await File.preview(files, { initialIndex: 1 }); }

initialIndex is zero-based and defaults to 0. An array with one file works on both platforms. On Android, passing multiple files makes File.canPreview() resolve to false and File.preview() reject.

File.preview() rejects if you pass title or mimeType with multiple files. File.canPreview() rejects if you pass mimeType with multiple files. For a single file, mimeType defaults to the file's type. Pass it explicitly if the extension does not identify the type, particularly on Android.

File.canPreview() resolves to false when the platform cannot preview, or when a checked file is missing or has an unsupported type. It rejects if it encounters an invalid or unreadable file. File.preview() rejects if any file cannot be previewed or initialIndex is invalid. Both methods reject empty arrays.

Keep files available until the viewer closes. File.preview() resolves when the preview is presented or handed off to another app.

If a share sheet is useful in your app, you can compose this with expo-sharing when previewing fails:

example.ts
import { File, Paths } from 'expo-file-system'; import * as Sharing from 'expo-sharing'; // This can be a file created, picked, or downloaded earlier. const file = new File(Paths.cache, 'report.pdf'); try { await file.preview({ title: 'Report' }); } catch { if (await Sharing.isAvailableAsync()) { await Sharing.shareAsync(file.uri, { dialogTitle: 'Share report', mimeType: file.type || 'application/pdf', }); } }
Uploading files using expo/fetch

You can upload files as blobs directly with fetch built into the Expo package:

example.ts
import { fetch } from 'expo/fetch'; import { File, Paths } from 'expo-file-system'; const file = new File(Paths.cache, 'file.txt'); await file.write('Hello, world!'); const response = await fetch('https://example.com', { method: 'POST', body: file, });

Or using the FormData constructor:

example.ts
import { fetch } from 'expo/fetch'; import { File, Paths } from 'expo-file-system'; const file = new File(Paths.cache, 'file.txt'); await file.write('Hello, world!'); const formData = new FormData(); formData.append('data', file); const response = await fetch('https://example.com', { method: 'POST', body: formData, });
Moving and copying files
example.ts
import { Directory, File, Paths } from 'expo-file-system'; try { const file = new File(Paths.document, 'example.txt'); file.create(); console.log(file.uri); // '${documentDirectory}/example.txt' const copiedFile = new File(Paths.cache, 'example-copy.txt'); file.copy(copiedFile); console.log(copiedFile.uri); // '${cacheDirectory}/example-copy.txt' file.move(Paths.cache); console.log(file.uri); // '${cacheDirectory}/example.txt' file.move(new Directory(Paths.cache, 'newFolder')); console.log(file.uri); // '${cacheDirectory}/newFolder/example.txt' } catch (error) { console.error(error); }
Random-access reads with FileHandle

Use FileHandle for efficient, random-access reads of large files without loading the entire file into memory. Obtain a handle by calling file.open(), read or write at any position using the offset property, and always close the handle when finished.

example.ts
import { File, Paths, FileMode } from 'expo-file-system'; const file = new File(Paths.document, 'recording.wav'); const handle = file.open(FileMode.ReadOnly); // Read the WAV header (first 44 bytes) const header = handle.readBytesSync(44); const sampleRate = new DataView(header.buffer).getUint32(24, true); console.log(`Sample rate: ${sampleRate} Hz`); // Seek to a specific offset and read a chunk handle.offset = 1024; const chunk = await handle.readBytes(4096); console.log(`Read ${chunk.length} bytes from offset 1024`); // Read an entire file in 64 KB chunks handle.offset = 0; const CHUNK_SIZE = 64 * 1024; while (handle.offset! < handle.size!) { const data = await handle.readBytes(CHUNK_SIZE); // Process data... } handle.close();
Using legacy FileSystem API
example.ts
import * as FileSystem from 'expo-file-system/legacy'; import { File, Paths } from 'expo-file-system'; try { const file = new File(Paths.cache, 'example.txt'); const content = await FileSystem.readAsStringAsync(file.uri); console.log(content); } catch (error) { console.error(error); }
Listing directory contents recursively
example.ts
import { Directory, Paths } from 'expo-file-system'; function printDirectory(directory: Directory, indent: number = 0) { console.log(`${' '.repeat(indent)} + ${directory.name}`); const contents = directory.list(); for (const item of contents) { if (item instanceof Directory) { printDirectory(item, indent + 2); } else { console.log(`${' '.repeat(indent + 2)} - ${item.name} (${item.size} bytes)`); } } } try { printDirectory(new Directory(Paths.cache)); } catch (error) { console.error(error); }

API

No API data file found, sorry!