Documentation Index
Fetch the complete documentation index at: https://mintlify.com/LegendApp/legend-state/llms.txt
Use this file to discover all available pages before exploring further.
The AsyncStorage plugin persists observable data to React Native’s AsyncStorage. This is the standard persistence solution for React Native applications.
Installation
npm install @legendapp/state @react-native-async-storage/async-storage
Setup
import { configureObservableSync } from '@legendapp/state/sync'
import { ObservablePersistAsyncStorage } from '@legendapp/state/persist-plugins/async-storage'
import AsyncStorage from '@react-native-async-storage/async-storage'
configureObservableSync({
persist: {
plugin: ObservablePersistAsyncStorage,
asyncStorage: {
AsyncStorage
}
}
})
Configuration
asyncStorage.AsyncStorage
AsyncStorageStatic
required
The AsyncStorage instance from @react-native-async-storage/async-storageimport AsyncStorage from '@react-native-async-storage/async-storage'
asyncStorage: {
AsyncStorage
}
Preload data on initialization for faster startup// Preload all keys
asyncStorage: {
AsyncStorage,
preload: true
}
// Preload specific keys
asyncStorage: {
AsyncStorage,
preload: ['user', 'settings', 'cart']
}
Usage
Basic Usage
import { synced } from '@legendapp/state/sync'
const user$ = synced({
get: () => api.getUser(),
persist: {
name: 'user'
// Uses globally configured AsyncStorage plugin
}
})
With Preload
import AsyncStorage from '@react-native-async-storage/async-storage'
import { configureObservableSync } from '@legendapp/state/sync'
import { ObservablePersistAsyncStorage } from '@legendapp/state/persist-plugins/async-storage'
configureObservableSync({
persist: {
plugin: ObservablePersistAsyncStorage,
asyncStorage: {
AsyncStorage,
preload: ['user', 'settings'] // Fast startup
}
}
})
const user$ = synced({
get: () => api.getUser(),
persist: { name: 'user' }
})
const settings$ = synced({
initial: { theme: 'light' },
persist: { name: 'settings' }
})
// Both are already loaded from preload
Plugin API
The AsyncStorage plugin implements the ObservablePersistPlugin interface:
initialize()
async initialize(config: ObservablePersistPluginOptions): Promise<void>
Initializes the plugin and preloads data if configured.
loadTable()
async loadTable(table: string): Promise<void>
Loads a specific key from AsyncStorage.
getTable()
getTable<T>(table: string, init: object): T
Gets the cached data for a key.
set()
async set(table: string, changes: Change[]): Promise<void>
Applies changes and saves to AsyncStorage.
getMetadata(table: string): PersistMetadata
Retrieves sync metadata.
async setMetadata(table: string, metadata: PersistMetadata): Promise<void>
Saves sync metadata.
deleteTable()
async deleteTable(table: string): Promise<void>
Removes data from AsyncStorage.
async deleteMetadata(table: string): Promise<void>
Removes metadata from AsyncStorage.
Examples
Complete App Setup
import React from 'react'
import { configureObservableSync } from '@legendapp/state/sync'
import { ObservablePersistAsyncStorage } from '@legendapp/state/persist-plugins/async-storage'
import AsyncStorage from '@react-native-async-storage/async-storage'
// Configure at app startup
configureObservableSync({
persist: {
plugin: ObservablePersistAsyncStorage,
asyncStorage: {
AsyncStorage,
preload: true // Preload all keys
}
}
})
export default function App() {
return <YourApp />
}
User Session
import { synced } from '@legendapp/state/sync'
import { when } from '@legendapp/state'
const user$ = synced({
get: () => api.getUser(),
persist: { name: 'user' }
})
// Wait for user to load
await when(syncState(user$).isPersistLoaded)
if (user$.get()) {
// User is logged in
navigateToHome()
} else {
// No user, show login
navigateToLogin()
}
App Settings
import { synced } from '@legendapp/state/sync'
import { observer } from '@legendapp/state/react'
const settings$ = synced({
initial: {
theme: 'light',
notifications: true,
language: 'en'
},
persist: { name: 'app-settings' }
})
const SettingsScreen = observer(function SettingsScreen() {
return (
<View>
<Switch
value={settings$.theme.get() === 'dark'}
onValueChange={(value) =>
settings$.theme.set(value ? 'dark' : 'light')
}
/>
<Text>Dark Mode</Text>
</View>
)
})
import { synced } from '@legendapp/state/sync'
interface CartItem {
id: string
name: string
quantity: number
price: number
}
const cart$ = synced<{ items: CartItem[] }>({
initial: { items: [] },
persist: { name: 'shopping-cart' }
})
// Add to cart
function addToCart(item: CartItem) {
const items = cart$.items.get()
const existing = items.find(i => i.id === item.id)
if (existing) {
existing.quantity++
cart$.items.set([...items])
} else {
cart$.items.push(item)
}
}
// Clear cart
function clearCart() {
cart$.items.set([])
}
Multi-Account
const currentUserId$ = observable<string | null>(null)
const userData$ = synced({
get: async () => {
const userId = currentUserId$.get()
if (!userId) return null
return api.getUserData(userId)
},
persist: {
name: computed(() => {
const userId = currentUserId$.get()
return userId ? `user-data-${userId}` : 'user-data'
})
}
})
// Switch accounts
currentUserId$.set('user-123')
// Loads user-data-user-123 from AsyncStorage
Offline Support
import { synced } from '@legendapp/state/sync'
import { observer } from '@legendapp/state/react'
import NetInfo from '@react-native-community/netinfo'
const isOnline$ = observable(true)
NetInfo.addEventListener(state => {
isOnline$.set(state.isConnected ?? false)
})
const todos$ = synced({
get: () => api.getTodos(),
set: ({ value }) => api.saveTodos(value),
persist: {
name: 'todos',
retrySync: true // Retry when back online
},
waitFor: () => isOnline$.get() // Wait for connection
})
const TodoScreen = observer(function TodoScreen() {
if (!isOnline$.get()) {
return <Text>Offline - changes will sync when online</Text>
}
return (
<FlatList
data={todos$.get()}
renderItem={({ item }) => <TodoItem item={item} />}
/>
)
})
Clear User Data on Logout
import { syncState } from '@legendapp/state/sync'
const user$ = synced({
persist: { name: 'user' }
})
const cart$ = synced({
persist: { name: 'cart' }
})
async function logout() {
// Clear persisted data
await Promise.all([
syncState(user$).resetPersistence(),
syncState(cart$).resetPersistence()
])
// Reset observables
user$.set(null)
cart$.set({ items: [] })
// Navigate to login
navigation.navigate('Login')
}
Data is stored as JSON strings:
const user$ = synced({
initial: { name: 'John', email: 'john@example.com' },
persist: { name: 'user' }
})
// AsyncStorage contents:
// key: 'user'
// value: '{"name":"John","email":"john@example.com"}'
// Metadata stored separately:
// key: 'user__m'
// value: '{"lastSync":1699564800000,"pending":{}}'
Without Preload
// Each observable loads individually (slower startup)
configureObservableSync({
persist: {
plugin: ObservablePersistAsyncStorage,
asyncStorage: { AsyncStorage }
}
})
const user$ = synced({ persist: { name: 'user' } })
const settings$ = synced({ persist: { name: 'settings' } })
const cart$ = synced({ persist: { name: 'cart' } })
// 3 separate AsyncStorage.getItem() calls
With Preload
// Load all keys at once (faster startup)
configureObservableSync({
persist: {
plugin: ObservablePersistAsyncStorage,
asyncStorage: {
AsyncStorage,
preload: ['user', 'settings', 'cart']
}
}
})
const user$ = synced({ persist: { name: 'user' } })
const settings$ = synced({ persist: { name: 'settings' } })
const cart$ = synced({ persist: { name: 'cart' } })
// 1 AsyncStorage.multiGet() call
Best Practices
- Use preload: Load critical data on app startup
- Namespace keys: Use descriptive, unique names
- Handle errors: AsyncStorage operations can fail
- Consider MMKV: For better performance, use MMKV plugin
- Clear on logout: Remove sensitive data when user logs out
When to Use
Use AsyncStorage when:
- Standard React Native persistence is sufficient
- Data size is moderate (< 6MB)
- You don’t need synchronous access
- Cross-platform compatibility is important
Use MMKV when:
- You need better performance
- Synchronous access is required
- Data is accessed frequently
See Also