Skip to main content

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
warning

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​

FeatureAsyncStorageMMKV
SpeedGoodExcellent
Expo Go CompatibleYesNo (dev build required)
EncryptionNoYes
Native SetupMinimalPod install required
Memory UsageHigherLower

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:

KeyDescription
flagsCurrent feature flag values
etagDefinitions revision for cache validation (If-None-Match and WebSocket ?rev=)
identityCurrent user identity
lastSyncTimestamp 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:

  1. SDK loads flags from storage cache
  2. If no cache exists, uses featureDefaults
  3. 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​

  1. Always provide featureDefaults - Ensures flags work even with empty cache
  2. Use MMKV for production - Better performance, especially on Android
  3. Use AsyncStorage for Expo Go development - Easier setup, no native code
  4. Consider encryption for sensitive flags - Use MMKV with encryption key
  5. Handle storage errors gracefully - Report onError so storage failures are visible in production