Storage
The React Native SDK supports pluggable storage adapters for persistent caching of feature flags. This enables offline support and faster app startup.
Why Use Storage?
Storage adapters provide several benefits:
- Offline support: Feature flags are available even without network
- Faster startup: Flags load instantly from cache before API refresh
- Reduced API calls: ETag-based caching minimizes network requests
- Consistent experience: Users see the same flags across app restarts
AsyncStorage Adapter
Best for Expo and when simplicity is preferred.
Installation
npm install @ops-ai/react-native-toggly-storage-async @react-native-async-storage/async-storage
For Expo:
npx expo install @react-native-async-storage/async-storage
npm install @ops-ai/react-native-toggly-storage-async
Usage
import { TogglyProvider } from '@ops-ai/react-native-toggly';
import { createAsyncStorageAdapter } from '@ops-ai/react-native-toggly-storage-async';
const storage = createAsyncStorageAdapter();
function App() {
return (
<TogglyProvider
appKey="your-app-key"
environment="production"
storage={storage}
>
<MyApp />
</TogglyProvider>
);
}
Options
const storage = createAsyncStorageAdapter({
keyPrefix: 'myapp_toggly_', // Custom key prefix (default: '@toggly/')
});
MMKV Adapter
Best for performance-critical applications. MMKV is significantly faster than AsyncStorage.
Installation
npm install @ops-ai/react-native-toggly-storage-mmkv react-native-mmkv
cd ios && pod install
MMKV requires native code and won't work with Expo Go. You'll need a development build for Expo.
Usage
import { TogglyProvider } from '@ops-ai/react-native-toggly';
import { createMMKVStorageAdapter } from '@ops-ai/react-native-toggly-storage-mmkv';
const storage = createMMKVStorageAdapter();
function App() {
return (
<TogglyProvider
appKey="your-app-key"
environment="production"
storage={storage}
>
<MyApp />
</TogglyProvider>
);
}
Options
import { MMKV } from 'react-native-mmkv';
import { createMMKVStorageAdapter } from '@ops-ai/react-native-toggly-storage-mmkv';
// With encryption
const storage = createMMKVStorageAdapter({
encryptionKey: 'your-secret-key',
});
// With custom MMKV instance
const mmkv = new MMKV({ id: 'toggly-storage' });
const storage = createMMKVStorageAdapter({ mmkv });
// Combined options
const storage = createMMKVStorageAdapter({
mmkv: new MMKV({ id: 'my-app-toggly' }),
encryptionKey: 'secret',
});
Comparison
| Feature | AsyncStorage | MMKV |
|---|---|---|
| Speed | Good | Excellent |
| Expo Go Compatible | Yes | No (dev build required) |
| Encryption | No | Yes |
| Native Setup | Minimal | Pod install required |
| Memory Usage | Higher | Lower |
Custom Storage Adapter
You can create a custom storage adapter by implementing the TogglyStorage interface:
import { TogglyStorage } from '@ops-ai/react-native-toggly-core';
class MyCustomStorage implements TogglyStorage {
async get(key: string): Promise<string | null> {
// Retrieve value from your storage
return yourStorageSystem.get(key);
}
async set(key: string, value: string): Promise<void> {
// Store value in your storage
await yourStorageSystem.set(key, value);
}
async delete(key: string): Promise<void> {
// Remove value from your storage
await yourStorageSystem.remove(key);
}
}
// Usage
const storage = new MyCustomStorage();
<TogglyProvider storage={storage}>
<App />
</TogglyProvider>
Storage Behavior
Cache Keys
To limit growth when many identities are cached, set maxCacheKeys on
TogglyProvider. See Client-side cache limits.
The SDK stores several keys in storage:
| Key | Description |
|---|---|
flags | Current feature flag values |
etag | Definitions revision for cache validation (If-None-Match and WebSocket ?rev=) |
identity | Current user identity |
lastSync | Timestamp of last sync |
Cache Invalidation
The cache is automatically invalidated when:
- User identity changes (
setIdentity) - Manual refresh is called (
refresh()) - ETag doesn't match server response (definitions revision changed)
See WebSocket sync for how revision caching works with live updates.
Offline Behavior
When the device is offline:
- SDK loads flags from storage cache
- If no cache exists, uses
featureDefaults - When connectivity returns, flags are automatically refreshed
Storage Errors
Storage adapters propagate read and write failures to the core SDK. Provide
TogglyProvider.onError to report those failures to your monitoring provider:
<TogglyProvider
appKey="your-app-key"
environment="production"
storage={storage}
onError={(error) => {
crashlytics().recordError(error);
}}
>
<App />
</TogglyProvider>
If a storage operation fails during refresh, the SDK keeps the last-known-good in-memory flags when available and reports the failure rather than silently discarding it.
Best Practices
- Always provide
featureDefaults- Ensures flags work even with empty cache - Use MMKV for production - Better performance, especially on Android
- Use AsyncStorage for Expo Go development - Easier setup, no native code
- Consider encryption for sensitive flags - Use MMKV with encryption key
- Handle storage errors gracefully - Report
onErrorso storage failures are visible in production