0
# Persistence
1
2
Comprehensive persistence system for offline-first applications with local storage and remote sync capabilities. Legend State's persistence layer provides automatic synchronization, conflict resolution, and supports multiple storage backends.
3
4
## Capabilities
5
6
### Core Persistence Functions
7
8
#### Persist Observable
9
10
Makes observables persistent with automatic synchronization.
11
12
```typescript { .api }
13
/**
14
* Makes an observable persistent with configurable storage and sync
15
* @param obs - Observable to make persistent
16
* @param config - Persistence configuration
17
*/
18
function persistObservable<T>(
19
obs: Observable<T>,
20
config: ObservablePersistenceConfig<T>
21
): void;
22
23
/**
24
* Creates a new persistent observable with initial state
25
* @param state - Initial state value
26
* @param config - Persistence configuration
27
* @returns Persistent observable
28
*/
29
function persistState<T>(
30
state: T,
31
config: ObservablePersistenceConfig<T>
32
): Observable<T>;
33
```
34
35
#### Configuration
36
37
Global persistence configuration and setup.
38
39
```typescript { .api }
40
/**
41
* Configures global persistence settings
42
* @param config - Global persistence configuration
43
*/
44
function configureObservablePersistence(
45
config: ObservablePersistenceConfig
46
): void;
47
48
/**
49
* Maps multiple persistence configurations
50
* @param persistences - Array of persistence configs to apply
51
*/
52
function mapPersistences<T>(
53
persistences: ObservablePersistenceConfig<T>[]
54
): void;
55
```
56
57
### Remote Change Detection
58
59
Functions for detecting and handling remote data changes.
60
61
```typescript { .api }
62
/**
63
* Checks if currently processing a remote change
64
* @returns True if in remote change context
65
*/
66
function isInRemoteChange(): boolean;
67
68
/**
69
* Listens for changes originating from remote sources
70
* @param obs - Observable to listen to
71
* @param callback - Function called for remote changes
72
*/
73
function onChangeRemote<T>(
74
obs: Observable<T>,
75
callback: (value: T) => void
76
): void;
77
```
78
79
### Field Transformation
80
81
Utilities for transforming field names during persistence operations.
82
83
```typescript { .api }
84
/**
85
* Inverts a field mapping object (keys become values, values become keys)
86
* @param fieldMap - Field mapping to invert
87
* @returns Inverted field mapping
88
*/
89
function invertFieldMap(
90
fieldMap: Record<string, string>
91
): Record<string, string>;
92
93
/**
94
* Transforms object field names using a field mapping
95
* @param obj - Object to transform
96
* @param fieldMap - Mapping of old field names to new field names
97
* @returns Object with transformed field names
98
*/
99
function transformObject<T>(
100
obj: T,
101
fieldMap: Record<string, string>
102
): T;
103
104
/**
105
* Transforms a property path using field mapping
106
* @param path - Property path as string array
107
* @param fieldMap - Field name mapping
108
* @returns Transformed path
109
*/
110
function transformPath(
111
path: string[],
112
fieldMap: Record<string, string>
113
): string[];
114
```
115
116
## Persistence Configuration
117
118
```typescript { .api }
119
interface ObservablePersistenceConfig<T = any> {
120
/** Unique key for this persistence */
121
name?: string;
122
/** Local storage plugin configuration */
123
local?: LocalPersistenceConfig;
124
/** Remote storage plugin configuration */
125
remote?: RemotePersistenceConfig;
126
/** Field transformations for data mapping */
127
transform?: TransformConfig;
128
/** Initial data to use if no persisted data exists */
129
initial?: T;
130
/** Whether to persist immediately on changes */
131
persistImmediately?: boolean;
132
/** Debounce time for persistence operations */
133
debounceTime?: number;
134
/** Function to generate unique IDs for new items */
135
generateId?: () => string;
136
/** Custom retry configuration */
137
retry?: RetryConfig;
138
}
139
140
interface LocalPersistenceConfig {
141
/** Storage plugin to use */
142
plugin: StoragePlugin;
143
/** Storage key prefix */
144
prefix?: string;
145
/** Whether to persist metadata */
146
persistMetadata?: boolean;
147
}
148
149
interface RemotePersistenceConfig {
150
/** Remote sync plugin configuration */
151
plugin: RemotePlugin;
152
/** URL or endpoint for remote sync */
153
url?: string;
154
/** Headers to include in requests */
155
headers?: Record<string, string>;
156
/** Sync mode: 'auto' | 'manual' */
157
mode?: 'auto' | 'manual';
158
/** Conflict resolution strategy */
159
conflictResolution?: 'client' | 'server' | 'merge';
160
}
161
162
interface TransformConfig {
163
/** Field name mappings */
164
fields?: Record<string, string>;
165
/** Value transformation functions */
166
values?: {
167
[field: string]: {
168
parse: (value: any) => any;
169
stringify: (value: any) => any;
170
};
171
};
172
}
173
174
interface RetryConfig {
175
/** Maximum number of retry attempts */
176
maxAttempts?: number;
177
/** Delay between retries in milliseconds */
178
delay?: number;
179
/** Exponential backoff multiplier */
180
backoff?: number;
181
}
182
```
183
184
## Storage Plugins
185
186
Pre-built storage plugins for different platforms and use cases.
187
188
```typescript { .api }
189
/** Local Storage plugin for web browsers */
190
declare const localStoragePlugin: StoragePlugin;
191
192
/** IndexedDB plugin for web browsers with larger storage */
193
declare const indexedDBPlugin: StoragePlugin;
194
195
/** AsyncStorage plugin for React Native */
196
declare const asyncStoragePlugin: StoragePlugin;
197
198
/** MMKV plugin for React Native high-performance storage */
199
declare const mmkvPlugin: StoragePlugin;
200
201
/** Firebase Realtime Database plugin */
202
declare const firebasePlugin: RemotePlugin;
203
204
/** Fetch-based remote plugin for REST APIs */
205
declare const fetchPlugin: RemotePlugin;
206
207
/** Query plugin for URL-based state persistence */
208
declare const queryPlugin: StoragePlugin;
209
210
interface StoragePlugin {
211
get(key: string): Promise<any>;
212
set(key: string, value: any): Promise<void>;
213
delete(key: string): Promise<void>;
214
getMetadata?(key: string): Promise<any>;
215
setMetadata?(key: string, metadata: any): Promise<void>;
216
}
217
218
interface RemotePlugin {
219
load(config: RemotePersistenceConfig): Promise<any>;
220
save(value: any, config: RemotePersistenceConfig): Promise<void>;
221
subscribe?(
222
config: RemotePersistenceConfig,
223
callback: (value: any) => void
224
): () => void;
225
}
226
```
227
228
**Usage Examples:**
229
230
```typescript
231
import { observable, persistObservable } from "@legendapp/state";
232
import { persistState } from "@legendapp/state/persist";
233
import {
234
localStoragePlugin,
235
fetchPlugin,
236
firebasePlugin
237
} from "@legendapp/state/persist-plugins";
238
239
// Simple local persistence
240
const user$ = observable({ name: "", email: "", preferences: {} });
241
242
persistObservable(user$, {
243
name: "user",
244
local: {
245
plugin: localStoragePlugin
246
}
247
});
248
249
// Local + Remote sync with conflict resolution
250
const todos$ = persistState([], {
251
name: "todos",
252
local: {
253
plugin: localStoragePlugin,
254
prefix: "myapp"
255
},
256
remote: {
257
plugin: fetchPlugin,
258
url: "https://api.example.com/todos",
259
mode: "auto",
260
conflictResolution: "merge",
261
headers: {
262
"Authorization": "Bearer token123"
263
}
264
},
265
transform: {
266
fields: {
267
"isCompleted": "done",
268
"description": "text"
269
}
270
},
271
retry: {
272
maxAttempts: 3,
273
delay: 1000,
274
backoff: 2
275
}
276
});
277
278
// Firebase real-time sync
279
const chat$ = persistState({ messages: [], users: [] }, {
280
name: "chat",
281
remote: {
282
plugin: firebasePlugin,
283
url: "https://myapp.firebaseio.com/chat"
284
}
285
});
286
287
// Field transformation example
288
const apiData$ = persistState({ user_id: 1, full_name: "John" }, {
289
name: "user",
290
local: {
291
plugin: localStoragePlugin
292
},
293
transform: {
294
fields: {
295
"user_id": "userId",
296
"full_name": "name"
297
},
298
values: {
299
"createdAt": {
300
parse: (dateString) => new Date(dateString),
301
stringify: (date) => date.toISOString()
302
}
303
}
304
}
305
});
306
307
// Listening for remote changes
308
onChangeRemote(todos$, (newTodos) => {
309
console.log("Todos updated from remote source:", newTodos);
310
// Handle remote updates (show notification, etc.)
311
});
312
313
// Manual sync control
314
const settings$ = persistState({ theme: "light" }, {
315
name: "settings",
316
local: { plugin: localStoragePlugin },
317
remote: {
318
plugin: fetchPlugin,
319
url: "/api/settings",
320
mode: "manual"
321
}
322
});
323
324
// Trigger manual sync
325
settings$.theme.set("dark");
326
// Will persist locally immediately, but won't sync to remote until:
327
// (Manual sync would be triggered by the app as needed)
328
```