Persist data on the device — key-value settings with AsyncStorage, secrets in the secure keychain with expo-secure-store, and structured data in a local SQLite database.
Why: AsyncStorage is the simple, async, persistent key-value store — the mobile equivalent of localStorage. Use it for preferences, a cached value, an onboarding-seen flag. Note: it is NOT encrypted, so never store tokens or passwords here.
// npx expo install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage'
// Values must be strings — JSON.stringify objects
await AsyncStorage.setItem('theme', 'dark')
const theme = await AsyncStorage.getItem('theme') // 'dark' or null
await AsyncStorage.removeItem('theme')Why: tokens, keys, and passwords belong in the device's encrypted keychain (iOS) / keystore (Android), not plain storage. expo-secure-store wraps both behind the same API. Reach for this for anything sensitive.
// npx expo install expo-secure-store
import * as SecureStore from 'expo-secure-store'
// Encrypted at rest by the OS keychain/keystore
await SecureStore.setItemAsync('auth_token', token)
const saved = await SecureStore.getItemAsync('auth_token')
await SecureStore.deleteItemAsync('auth_token')Why: AsyncStorage only stores strings, so you repeat JSON.stringify/parse everywhere. A tiny generic wrapper centralizes that and gives you type safety — the kind of helper every real app grows.
import AsyncStorage from '@react-native-async-storage/async-storage'
async function save<T>(key: string, value: T) {
await AsyncStorage.setItem(key, JSON.stringify(value))
}
async function load<T>(key: string): Promise<T | null> {
const raw = await AsyncStorage.getItem(key)
return raw ? (JSON.parse(raw) as T) : null
}Why: when you have real relational data to query offline — a notes app, an offline cache of records — a key-value store is not enough. expo-sqlite gives you a full local SQL database. Use it when you need queries, relationships, or large datasets on device.
// npx expo install expo-sqlite
import * as SQLite from 'expo-sqlite'
const db = await SQLite.openDatabaseAsync('app.db')
await db.execAsync(
'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT);'
)
await db.runAsync('INSERT INTO notes (body) VALUES (?)', 'First note')
const rows = await db.getAllAsync('SELECT * FROM notes')