React Native 36,878 has no localStorage, no IndexedDB and no cookies jar you control. Every store is a native module, and they differ in what they hold, whether calls are synchronous, and whether data is encrypted:
| Store | Package (version here) | Calls | Holds | Use it for |
|---|---|---|---|---|
| AsyncStorage 5,070 | @react-native-async-storage/async-storage 2.2.0 | Async | Strings | Existing code, small settings |
| SQLite 4,756 key-value | expo-sqlite/kv-store 57.0.3 | Sync or async | Strings | An AsyncStorage drop-in |
| MMKV 18,748 | react-native-mmkv 4.3.2 8,507 | Sync | Strings, numbers, booleans, bytes | Settings, persisted state |
| SQLite | expo-sqlite 57.0.3 6,418 | Sync or async | Tables, SQL | Notes, orders, anything queried |
| Secure store | expo-secure-store 57.0.4 | Async (sync too) | Small strings | Tokens and secrets |
Beyond these, op-sqlite 1,044 (https://github.com/OP-Engineering/op-sqlite 1,044 ) (18.2.5) is a fast SQLite binding over JSI, Drizzle 317,128 ORM (https://github.com/drizzle-team/drizzle-orm 35,908 ) (0.45.3, Apache-2.0) adds typed queries and migrations on top of expo-sqlite, and expo-file-system stores whole files such as downloaded covers. To feel the differences, BookNest's demo screen writes and reads the same 500 small JSON strings through each store:
t = performance.now();
for (let i = 0; i < N; i++) prefs.set(`k${i}`, value);
for (let i = 0; i < N; i++) prefs.getString(`k${i}`);
console.log('MMKV:', ms(t));LOG AsyncStorage (await each): 6986 ms LOG expo-sqlite kv-store (sync): 730 ms LOG MMKV: 3 ms LOG SecureStore, 20 + 20 only: 428 ms
This was a debug build on a busy emulator, and the first run, which created the keys, was slower still (12,047, 6,096, 20 and 2,045 ms), so read the ratios, not the numbers. Awaiting AsyncStorage one call at a time is the slow pattern: its multiSet and multiGet batch many keys into one call. All of these files live in the app's private directory (files/mmkv/, files/SQLite/, and databases/RKStorage for AsyncStorage).