When AsyncStorage isn't enough — you're caching API responses, saving generated files, or handling downloads — you need the file system. expo-file-system gives you direct access to device storage, and SDK 54 introduced a new synchronous File / Paths API that reads like ordinary file code. This guide covers writing, reading, deleting, JSON caching, and directories.
Install
npx expo install expo-file-system
Write and read a text file
The new API models files as objects you create, write, and read:
import { File, Paths } from "expo-file-system"
const file = new File(Paths.document, "notes.txt")
file.create()
file.write("Hello from Expo!")
console.log(file.textSync()) // "Hello from Expo!"
Use file.textSync() for a quick synchronous read, or await file.text() when you'd rather not block.
Document vs cache storage
Choose the right base path:
Paths.document— persistent user data (survives restarts, gets backed up).Paths.cache— disposable data the OS may purge when storage is low.
const persistent = new File(Paths.document, "profile.json")
const temporary = new File(Paths.cache, "thumb.jpg")
Cache JSON offline
A tiny offline cache without a database — write stringified JSON, read it back parsed:
import { File, Paths } from "expo-file-system"
const cache = new File(Paths.document, "articles.json")
export function saveArticles(articles: unknown) {
if (!cache.exists) cache.create()
cache.write(JSON.stringify(articles))
}
export function loadArticles() {
if (!cache.exists) return null
return JSON.parse(cache.textSync())
}
Check, delete, and inspect files
const file = new File(Paths.document, "notes.txt")
console.log(file.exists) // boolean
console.log(file.size) // bytes
file.delete() // remove it
Work with directories
Create folders and list their contents with the Directory class:
import { Directory, Paths } from "expo-file-system"
const dir = new Directory(Paths.document, "downloads")
if (!dir.exists) dir.create()
for (const entry of dir.list()) {
console.log(entry.name) // files and subdirectories
}
Gotchas
- Call
create()beforewrite()on a new file, or guard withif (!file.exists). - Don't hardcode paths — always build from
Paths.document/Paths.cache, since the sandbox location differs per install and platform. - For tiny key/value data (flags, IDs), AsyncStorage is simpler; for secrets, use expo-secure-store.
Next steps
Storing lots of structured, queryable data? Move up to expo-sqlite. Saving files users picked or captured? See the expo-image-picker guide. Full list: top 10 Expo SDK modules.