Lightning-fast native storage for Expo.
npm install expo-native-storage

Lightning-fast native storage for Expo using UserDefaults and SharedPreferences.
- Fast: Direct native storage, no bridge delays
- Tiny: 19x smaller bundle size compared to AsyncStorage
- Scales: Gets faster with more operations (up to 32x on Android)
- Web support: Falls back to localStorage
- Native: Uses UserDefaults (iOS) & SharedPreferences (Android)
``bash`bun
bunx expo install expo-native-storagenpm
npx expo install expo-native-storage
> [!IMPORTANT]
> After installation, you must rebuild your app to link the native module. This module requires native code and will not work with Expo Go. You need a development build or production build.
`bash`For development builds
npx expo prebuild --clean
npx expo run:iosor
npx expo run:android
This module requires native code and will not work with Expo Go. You need a development build or production build.
`typescript
import Storage from 'expo-native-storage';
// Store strings
await Storage.setItem('username', 'john_doe');
const username = await Storage.getItem('username'); // 'john_doe'
// Store objects
await Storage.setObject('user', {
name: 'John',
age: 30,
premium: true
});
const user = await Storage.getObject('user');
// Remove items
await Storage.removeItem('username');
// Clear all
await Storage.clear();
`
`typescript
import Storage from 'expo-native-storage';
// Store strings synchronously
Storage.setItemSync('username', 'john_doe');
const username = Storage.getItemSync('username'); // 'john_doe'
// Store objects synchronously
Storage.setObjectSync('user', {
name: 'John',
age: 30,
premium: true
});
const user = Storage.getObjectSync('user');
// Remove items synchronously
Storage.removeItemSync('username');
// Clear all synchronously
Storage.clearSync();
`
When to use sync vs async:
- Sync methods: App initialization, reading config on startup, settings screens
- Async methods: When you need to coordinate with other async operations, or prefer Promise-based code
`typescript
// Save theme preference
await Storage.setItem('theme', 'dark');
// Load on app start
const theme = await Storage.getItem('theme') || 'light';
`
| Method | Description | Return |
|--------|-------------|---------|
| setItem(key, value) | Store a string value | Promise |getItem(key)
| | Get a string value | Promise |setObject(key, object)
| | Store an object as JSON | Promise |getObject
| | Get an object from JSON | Promise |removeItem(key)
| | Remove an item | Promise |clear()
| | Remove all items | Promise |multiGet(keys)
| | Get multiple items at once | Promise |multiSet(items)
| | Set multiple items at once | Promise |
| Method | Description | Return |
|--------|-------------|---------|
| setItemSync(key, value) | Store a string value | void |getItemSync(key)
| | Get a string value | string \| null |setObjectSync(key, object)
| | Store an object as JSON | void |getObjectSync
| | Get an object from JSON | T \| null |removeItemSync(key)
| | Remove an item | void |clearSync()
| | Remove all items | void |
| Platform | Operations | expo-native-storage | AsyncStorage | Improvement |
|----------|------------|-------------------|--------------|-------------|
| Android Phone | 100 ops | 11ms | 165ms | 15x faster |
| | 200 ops | ~25ms | ~290ms | 11x faster |
| | 500 ops | ~50ms | ~650ms | 13x faster |
| | 1000 ops | ~95ms | ~1216ms | 13x faster |
| Android Emulator | 100 ops | 12ms | 219ms | 18x faster |
| iOS Phone | 100 ops | 72ms | 72ms | Same speed |
| Bundle Size | - | 19.6KB | 381KB | 19x smaller |
| Platform | Operations | Sync Methods | Async Methods | Improvement |
|----------|------------|--------------|---------------|-------------|
| Android Phone | 1000 ops | 98ms | 472ms | 4.8x faster |
| iOS Phone | 1000 ops | 1098ms | 1499ms | 1.4x faster |
Note on iOS Performance: iOS sync methods are limited by UserDefaults disk I/O (~1ms per write). For bulk operations (1000+ writes), consider using specialized libraries like react-native-mmkv. Sync methods are perfect for typical use cases like app settings and user preferences.
Tested on iPhone 17 Pro and Nothing Phone 3a with Android 15.
Emulator results from MacBook Pro M4 with 16GB RAM.
The performance advantage of expo-native-storage increases with usage:
Why? SharedPreferences uses in-memory caching after first access, while AsyncStorage hits the SQLite database for every operation. The more operations you perform, the bigger the performance gap becomes.
Perfect for:
- Settings screens with many preferences
- Offline data caching with frequent reads
- State persistence with frequent updates
- User session data with multiple keys
`typescript
// Before (AsyncStorage)
import AsyncStorage from '@react-native-async-storage/async-storage';
await AsyncStorage.setItem('key', 'value');
// After (expo-native-storage)
import Storage from 'expo-native-storage';
await Storage.setItem('key', 'value');
`
Drop-in replacement with zero breaking changes.
- iOS: Uses UserDefaults (synchronous, limited to ~1MB per key)SharedPreferences
- Android: Uses (synchronous, good for small data)localStorage
- Web: Uses (synchronous, ~5-10MB limit)
- Expo SDK 50+
- Development builds (not available in Expo Go)
This error means the native module isn't linked. To fix:
1. Rebuild your app after installing:
`bash`
bunx expo prebuild --clean
bunx expo run:ios # or run:android
2. Clear caches if the issue persists:
`bash`
# clear Metro bundler cache
bunx expo start -c
# clear all caches
rm -rf node_modules
bun install # or npm install
bunx expo prebuild --clean
3. Verify installation:
- Check that expo-native-storage is in your package.jsonExpoNativeStorage` appears in your Podfile.lock
- Make sure you're using a development build, not Expo Go
- For iOS: Check that
- For Android: Check that the module is in your build.gradle dependencies
Open an issue on GitHub with:
- Your Expo SDK version
- React Native version
- Platform (iOS/Android)
- Full error message
- Whether you're using Expo Go or a development build
MIT