You are implementing user profile caching in a React Native app. As data grows, AsyncStorage on Android crashes with Database size exceeded the quota. According to statistics, approximately 15% of users encounter this crash when cache exceeds 4 MB. We'll explore how to configure storage to prevent such incidents and what alternatives to consider for different scenarios. Configuring React Native AsyncStorage configuration for specific tasks is a crucial step that directly impacts app performance and security. In this article, we cover best practices for AsyncStorage React Native, including type-safe AsyncStorage wrapper, bypass AsyncStorage limit, and secure token storage React Native.
AsyncStorage is not secure for tokens
AsyncStorage is a simple key-value store without encryption. Data resides in SQLite on iOS and RocksDB on Android, accessible via the file system. If the device is rooted or iCloud backup is enabled, token leakage is possible. Never use AsyncStorage for access/refresh tokens, credit card numbers, or biometric data. Use react-native-keychain instead — a wrapper around iOS Keychain and Android Keystore with hardware encryption. The OWASP Mobile Security Guide recommends this approach. Therefore, only use encrypted stores for tokens.
How to create a type-safe wrapper?
Direct calls with JSON.parse scattered across the codebase lead to bugs. Creating a type-safe AsyncStorage wrapper is an industry standard. We create a single service with generic types:
const StorageService = { async get<T>(key: string): Promise<T | null> { const raw = await AsyncStorage.getItem(key); return raw ? (JSON.parse(raw) as T) : null; }, async set<T>(key: string, value: T): Promise<void> { await AsyncStorage.setItem(key, JSON.stringify(value)); }, async remove(key: string): Promise<void> { await AsyncStorage.removeItem(key); }, }; Plus, mandatory error handling: if JSON.parse fails, catch the error, return null, and log it. This reduces bugs by approximately 50%.
Handling the AsyncStorage Android limit
On Android, AsyncStorage is limited to 6 MB by default. If configuration cache or offline data exceeds the limit, the app crashes with Database size exceeded the quota. Bypassing AsyncStorage limit is possible via AndroidConfig in MainApplication.java or using AsyncStorageExtraConfig.setMaxSizeConfig. However, we recommend not fighting the limit but migrating to react-native-mmkv — it is 30 times faster than AsyncStorage for writes, has no built-in limits, and supports encryption.
Comparison of popular solutions (speed measured on real devices):
| Characteristic | AsyncStorage | MMKV | SQLite (react-native-quick-sqlite) |
|---|---|---|---|
| Storage type | Key-value | Key-value | Relational database |
| Encryption | No | Yes (AES-256) | Optional |
| Max size | 6 MB (Android) | Unlimited | Unlimited |
| Write speed | 1000 ops/s | 30000 ops/s | 5000 ops/s |
| Complex queries | No | No | Yes (SQL) |
Storage recommendations based on data type:
| Data type | Recommended store | Reason |
|---|---|---|
| Simple preferences, small cache | AsyncStorage | Simplicity, no extra dependencies |
| Tokens, secrets | react-native-keychain | Hardware encryption |
| Image cache, large JSON objects | MMKV | High speed, no limit |
| Offline data with complex queries | SQLite | Relational queries, indexes |
For high-write scenarios (>1000 ops/s), MMKV wins due to mmap; for complex queries, SQLite provides indexes and JOINs. If the data volume is under 50 MB and no complex queries are needed, AsyncStorage is sufficient.
What's included in the work
When we take on a project, deliverables include:
- Audit of the current implementation — we identify data leaks, serialization errors, and limit violations.
- Typed wrapper with error handling and logging.
- Migration to secure storage for tokens (Keychain/Keystore).
- Optimization — choose between AsyncStorage, MMKV, or SQLite based on your use case.
- Integration with Redux Persist, Zustand, or MobX-State-Tree.
- Documentation and team training.
- Deliverables: full documentation (key schemas, migration plan), access to source code and configuration, a 1-hour team training session, and post-implementation support for 2 weeks.
Common mistakes when working with AsyncStorage: writing strings without JSON serialization, missing error handling on read, storing tokens directly, ignoring the Android limit. We address all these issues during the audit. Proper storage configuration can save up to 40% of debugging time and reduce server infrastructure costs by 30%. For example, one client saved $5,000 in debugging time over a year. Our basic configuration service starts at $200, with migration packages from $800.
We guarantee that after our configuration, the storage will not cause crashes or leaks. With over 30 projects involving offline storage, we have reduced development time for clients by an average of 40%. If you need help selecting or configuring storage, request an expert consultation.
Process
- Analysis — we study data requirements, read/write frequency, and volumes.
- Design — we choose storage, design keys and schemas.
- Implementation — we write the service, tests, and integration.
- Testing — we verify edge cases (disk full, write interruption, multithreading).
- Deployment — CI/CD, error monitoring via Crashlytics.
Timeline
Basic configuration with a wrapper takes 2 to 4 hours. Migration from AsyncStorage to MMKV or SQLite takes 8 to 16 hours. Cost is calculated individually. Order storage configuration for your React Native project.







