React Native Cheatsheet
Storage
Use this React Native reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
AsyncStorage
Simple key-value storage that persists across app restarts. Async, string-only values.
npm install @react-native-async-storage/async-storage cd ios && pod install
Basic Operations
import AsyncStorage from '@react-native-async-storage/async-storage'; // Set await AsyncStorage.setItem('user_token', token); await AsyncStorage.setItem('user', JSON.stringify({ id: 1, name: 'Alice' })); // Get const token = await AsyncStorage.getItem('user_token'); const raw = await AsyncStorage.getItem('user'); const user = raw ? JSON.parse(raw) : null; // Remove await AsyncStorage.removeItem('user_token'); // Clear all await AsyncStorage.clear(); // Multi-get / multi-set const keys = ['@key1', '@key2', '@key3']; const pairs = await AsyncStorage.multiGet(keys); // pairs = [['@key1', 'val1'], ['@key2', 'val2'], ...] await AsyncStorage.multiSet([ ['@key1', 'value1'], ['@key2', 'value2'], ]); await AsyncStorage.multiRemove(['@key1', '@key2']); // Get all keys const allKeys = await AsyncStorage.getAllKeys(); // Merge (deep merge for JSON values) await AsyncStorage.mergeItem('user', JSON.stringify({ name: 'Bob' }));
Typed AsyncStorage Wrapper
const Storage = { async set<T>(key: string, value: T): Promise<void> { await AsyncStorage.setItem(key, JSON.stringify(value)); }, async get<T>(key: string): Promise<T | null> { const raw = await AsyncStorage.getItem(key); return raw ? (JSON.parse(raw) as T) : null; }, async remove(key: string): Promise<void> { await AsyncStorage.removeItem(key); }, }; // Usage await Storage.set('session', { token: 'abc', expiresAt: Date.now() + 3600000 }); const session = await Storage.get<{ token: string; expiresAt: number }>('session');
MMKV (High-performance alternative)
Synchronous, C++ native, ~30× faster than AsyncStorage.
npm install react-native-mmkv cd ios && pod install
import { MMKV } from 'react-native-mmkv'; const storage = new MMKV(); // Synchronous — no await storage.set('user.id', 42); storage.set('user.name', 'Alice'); storage.set('user.active', true); storage.set('token', 'abc123'); const id = storage.getNumber('user.id'); // number | undefined const name = storage.getString('user.name'); // string | undefined const active = storage.getBoolean('user.active'); // boolean | undefined storage.delete('token'); storage.clearAll(); const keys = storage.getAllKeys(); // Encrypted storage const secureStorage = new MMKV({ id: 'secure', encryptionKey: 'my-key' }); // JSON values storage.set('user', JSON.stringify({ id: 1, name: 'Alice' })); const user = JSON.parse(storage.getString('user') ?? 'null'); // Listen to changes storage.addOnValueChangedListener((changedKey) => { console.log('Changed:', changedKey, storage.getString(changedKey)); });
MMKV with Zustand (persist middleware)
import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import { MMKV } from 'react-native-mmkv'; const storage = new MMKV(); const mmkvStorage = { setItem: (name: string, value: string) => storage.set(name, value), getItem: (name: string) => storage.getString(name) ?? null, removeItem: (name: string) => storage.delete(name), }; const useStore = create( persist( (set) => ({ count: 0, increment: () => set(s => ({ count: s.count + 1 })) }), { name: 'app-store', storage: createJSONStorage(() => mmkvStorage) } ) );
Expo SecureStore (Keychain / Keystore)
Encrypted storage backed by iOS Keychain and Android Keystore.
npx expo install expo-secure-store
import * as SecureStore from 'expo-secure-store'; // Save await SecureStore.setItemAsync('auth_token', token); await SecureStore.setItemAsync('private_key', key, { keychainAccessible: SecureStore.WHEN_UNLOCKED, // Options: ALWAYS | WHEN_UNLOCKED | AFTER_FIRST_UNLOCK | ALWAYS_THIS_DEVICE_ONLY | WHEN_UNLOCKED_THIS_DEVICE_ONLY }); // Get const token = await SecureStore.getItemAsync('auth_token'); // Delete await SecureStore.deleteItemAsync('auth_token'); // Synchronous (Expo SDK 48+) SecureStore.setItem('key', 'value'); const value = SecureStore.getItem('key');
Keychain (RN CLI — react-native-keychain)
npm install react-native-keychain cd ios && pod install
import * as Keychain from 'react-native-keychain'; // Save credentials (username + password pair) await Keychain.setGenericPassword('alice@example.com', 'secret123'); // Retrieve const credentials = await Keychain.getGenericPassword(); if (credentials) { console.log(credentials.username); console.log(credentials.password); } // Reset await Keychain.resetGenericPassword(); // With biometric protection await Keychain.setGenericPassword('user', token, { accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY, accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED, });
SQLite
For relational/structured local data with queries.
# Expo npx expo install expo-sqlite # RN CLI npm install react-native-sqlite-storage
expo-sqlite (v14+, async API)
import * as SQLite from 'expo-sqlite'; // Open database const db = await SQLite.openDatabaseAsync('mydb.db'); // Execute DDL await db.execAsync(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL ); `); // Insert const result = await db.runAsync( 'INSERT INTO users (name, email) VALUES (?, ?)', ['Alice', 'alice@example.com'] ); console.log(result.lastInsertRowId); console.log(result.changes); // Select all const rows = await db.getAllAsync<{ id: number; name: string; email: string }>( 'SELECT * FROM users' ); // Select first row const user = await db.getFirstAsync<{ id: number }>( 'SELECT * FROM users WHERE email = ?', ['alice@example.com'] ); // Async iterator (large result sets) for await (const row of db.getEachAsync('SELECT * FROM users')) { process(row); } // Transaction await db.withTransactionAsync(async () => { await db.runAsync('INSERT INTO users (name, email) VALUES (?, ?)', ['Bob', 'bob@ex.com']); await db.runAsync('INSERT INTO users (name, email) VALUES (?, ?)', ['Carol', 'carol@ex.com']); }); // Close await db.closeAsync();
Drizzle ORM with SQLite (type-safe)
npm install drizzle-orm expo-sqlite npm install -D drizzle-kit
import { drizzle } from 'drizzle-orm/expo-sqlite'; import { openDatabaseSync } from 'expo-sqlite'; import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'; import { eq } from 'drizzle-orm'; const usersTable = sqliteTable('users', { id: integer('id').primaryKey({ autoIncrement: true }), name: text('name').notNull(), email: text('email').notNull().unique(), }); const expo = openDatabaseSync('db.db'); const db = drizzle(expo, { schema: { usersTable } }); // Insert await db.insert(usersTable).values({ name: 'Alice', email: 'alice@ex.com' }); // Select const users = await db.select().from(usersTable); const alice = await db.select().from(usersTable).where(eq(usersTable.email, 'alice@ex.com')); // Update await db.update(usersTable).set({ name: 'Alicia' }).where(eq(usersTable.id, 1)); // Delete await db.delete(usersTable).where(eq(usersTable.id, 1));
React Native File System
npx expo install expo-file-system
# or
npm install react-native-fsexpo-file-system
import * as FileSystem from 'expo-file-system'; const dir = FileSystem.documentDirectory; // app private directory // Write await FileSystem.writeAsStringAsync(`${dir}notes.txt`, 'Hello World'); // Read const content = await FileSystem.readAsStringAsync(`${dir}notes.txt`); // Download file const { uri } = await FileSystem.downloadAsync( 'https://example.com/file.pdf', `${dir}file.pdf`, { headers: { Authorization: `Bearer ${token}` } } ); // File info const info = await FileSystem.getInfoAsync(`${dir}notes.txt`); info.exists; info.size; info.modificationTime; // Delete await FileSystem.deleteAsync(`${dir}notes.txt`); // Directory listing const files = await FileSystem.readDirectoryAsync(dir); // Move / Copy await FileSystem.moveAsync({ from: srcUri, to: destUri }); await FileSystem.copyAsync({ from: srcUri, to: destUri }); // Directories await FileSystem.makeDirectoryAsync(`${dir}images`, { intermediates: true });
Storage locations
| Location | Expo | Notes |
|---|---|---|
| App private | FileSystem.documentDirectory | Persists, backed up (iOS) |
| Cache | FileSystem.cacheDirectory | May be purged by OS |
| External (Android) | MediaLibrary | Public photos/videos |
Redux Persist (state hydration from storage)
npm install redux-persist @reduxjs/toolkit react-redux
import { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistReducer } from 'redux-persist'; import AsyncStorage from '@react-native-async-storage/async-storage'; const persistConfig = { key: 'root', storage: AsyncStorage, whitelist: ['auth', 'settings'], // only persist these slices blacklist: ['ui'], // never persist these }; const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer }); export const persistor = persistStore(store); // In App.tsx import { PersistGate } from 'redux-persist/integration/react'; <Provider store={store}> <PersistGate loading={<ActivityIndicator />} persistor={persistor}> <App /> </PersistGate> </Provider>
Gotcha: AsyncStorage has a default max item size of ~2 MB and a total storage limit around 6 MB on older Android. Use MMKV or SQLite for large datasets.