# Storage
# Overview
| Storage Option | Type | Performance | Security | Best For | Offline Sync | Complexity | 
|---|---|---|---|---|---|---|
| AsyncStorage | Key-Value | ⚡ Low | ❌ No | Small data, flags, prefs | ❌ No | Easy | 
| MMKV | Key-Value | ⚡⚡⚡ Very High | ✅ Optional | Fast key-value storage | ❌ No | Easy | 
| SecureStore / Keychain | Key-Value (Secure) | ⚡ Medium | ✅ Encrypted | Tokens, passwords | ❌ No | Easy | 
| SQLite | Relational DB | ⚡⚡ Medium | ❌ Not by default | Structured data, queries | ❌ No | Medium | 
| Realm | Object DB | ⚡⚡ High | ✅ Encrypted option | Complex data models, offline apps | ✅ Yes | Medium | 
| WatermelonDB | ORM on SQLite | ⚡⚡ High | ❌ Not built-in | Large offline-first apps | ✅ Yes | Hard | 
👉 Quick Recommendations:
- Store user prefs, tokens → AsyncStorage or SecureStore.
- Need fast storage with encryption → MMKV.
- Need relational data (tables, queries) → SQLite.
- Need complex offline-first app with sync → Realm or WatermelonDB.
Recommended for Access Tokens
| Storage Option | Security | Notes | 
|---|---|---|
| SecureStore (Expo) | ✅ Encrypted | Uses iOS Keychain & Android Keystore. Easiest if you’re in Expo ecosystem. | 
| react-native-keychain | ✅ Encrypted | Community package, works in bare RN. Stores tokens in Keychain/Keystore. | 
| MMKV (Encrypted mode) | ⚠️ Medium | MMKV supports encryption, but it’s still app-managed. Safer than plain AsyncStorage, but weaker than OS-managed keystore. | 
| AsyncStorage | ❌ Insecure | Data is stored in plaintext on device, not safe for tokens. | 
# MMKV + React Query for Offline data
gamesCacheManager.ts
import { MMKV } from 'react-native-mmkv'
const storage = new MMKV()
export const gamesCacheManager = {
  getCachedItem: (locale: string) => {
    const value = storage.getString(`games_${locale}`)
    return value ? JSON.parse(value) : null
  },
  storeItem: (locale: string, games: any[]) => {
    storage.set(`games_${locale}`, JSON.stringify(games))
  },
  clearItem: (locale: string) => {
    storage.delete(`games_${locale}`)
  }
}
useAllGameQuery.ts
export function useAllGameQuery() {
  const { i18n } = useLingui()
  return useSuspenseQuery({
    queryKey: ['games', i18n.locale],
    initialData: () => {
      // Load from MMKV first
      return gamesCacheManager.getCachedItem(i18n.locale) || undefined
    },
    queryFn: async () => {
      const games = await getGames()
    gamesCacheManager.storeItem(i18n.locale, games)
      return games
    },
  })
}
Benefits of This Setup
- Cold start = instant data → MMKV gives you the previous snapshot.
- React Query still does refetching → so the cache stays fresh.
- Minimal complexity → just JSON stringify/parse around MMKV.
- Locale-aware → since you’re keying by i18n.locale.
← RN overview Links →