Persistent State
Zero-setup persistent state for miniapps. The SDK handles everything automatically.
Overview
The SDK provides a complete persistent state solution with no external dependencies:
- Vanilla store – No Zustand/Redux required
- AsyncStorage backend – Built-in local persistence
- Automatic lifecycle – Saves on app background, no hook needed
- Debounced saves – Coalesces rapid mutations (500ms)
- Miniapp isolation – Each miniapp's state is namespaced
- Cloud sync hooks – Optional callbacks to sync with your cloud backend
Using the hook
Inside miniapp components, use useNamedPersistentState for data that should survive app restarts:
import { useNamedPersistentState } from '@dpage-ai/react-native-dpage';
function Counter() {
const [count, setCount] = useNamedPersistentState('count', 0);
return (
<Button onPress={() => setCount(c => c + 1)}>
Count: {count}
</Button>
);
}When to use it
| Prop | Type | Default | Description |
|---|---|---|---|
useNamedPersistentState | Persistent | - | Data that must survive app restarts (progress, settings, drafts) |
useState | Ephemeral | - | Temporary UI state (modals, spinners, in-flight input) |
Keys should be descriptive: e.g. counter.value, settings.theme, user.profile.
Lifecycle flow
- App opens – Call
initLifecycleListener()once at startup - Miniapp screen opens –
restoreMiniapp(appId)loads persisted state - State mutations – Debounced saves (500ms) reduce redundant writes
- App backgrounds – Automatic save of all active miniapp states
App integration
For apps using the SDK, the setup is minimal:
// MiniappViewer.tsx
import {
MiniappStateProvider,
PersistenceManager
} from '@dpage-ai/react-native-dpage';
function MiniappViewer({ appId }) {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
PersistenceManager.restoreMiniapp(appId)
.finally(() => setIsReady(true));
}, [appId]);
if (!isReady) return <Loading />;
return (
<MiniappStateProvider miniappId={appId}>
<MiniappComponent miniappId={appId} />
</MiniappStateProvider>
);
}Enable Auto-Save
initLifecycleListener() once at app startup to enable automatic saves when the app goes to background.Cloud Sync
AsyncStorage is always the local source of truth. The SDK includes a built-in CloudSync client for syncing to DPage's cloud. State is synced via an edge-first architecture:
- Edge (Cloudflare KV) – Fast reads/writes at the edge
- PostgreSQL (Supabase) – Async durability sync for backup
This gives you sub-50ms latency for state operations.
import {
PersistenceManager,
CloudSync
} from '@dpage-ai/react-native-dpage';
// Use the built-in DPage cloud sync
PersistenceManager.setSyncCallbacks({
// Called when a miniapp closes (user navigates back)
onMiniappClose: async (miniappId) => {
const snapshot = await PersistenceManager.getSnapshot(miniappId);
if (snapshot) {
const result = await CloudSync.upload(miniappId, snapshot);
if (result.success) {
await PersistenceManager.markSynced(miniappId);
}
}
},
onAppForeground: async () => {
const unsynced = await PersistenceManager.getUnsyncedMiniapps();
for (const id of unsynced) {
const snapshot = await PersistenceManager.getSnapshot(id);
if (snapshot) {
await CloudSync.upload(id, snapshot);
await PersistenceManager.markSynced(id);
}
}
},
onAppBackground: async () => {
// Batch upload all unsynced states
const unsynced = await PersistenceManager.getUnsyncedMiniapps();
const states = await Promise.all(
unsynced.map(async (id) => ({
miniappId: id,
snapshot: await PersistenceManager.getSnapshot(id),
}))
);
await CloudSync.uploadBatch(states.filter(s => s.snapshot));
await PersistenceManager.markAllSynced();
},
});CloudSync API
CloudSync.upload(), CloudSync.download(), CloudSync.uploadBatch(), CloudSync.downloadBatch() – use your auth from setUserAuth({userId, authToken}). Enable cloud sync with cloudSync: true in initDPage().Sync Status
The SDK tracks sync status automatically:
// Check sync status
const status = await PersistenceManager.getSyncStatus(miniappId);
// { miniappId, lastLocalSave, lastCloudSync, needsSync }
// Get unsynced miniapps
const unsynced = await PersistenceManager.getUnsyncedMiniapps();
// Mark as synced after cloud upload
await PersistenceManager.markSynced(miniappId);
// Hydrate from cloud (updates both memory and AsyncStorage)
await PersistenceManager.hydrateFromCloud(miniappId, cloudSnapshot);Storage Structure
All data is stored in AsyncStorage with a clean, minimal structure:
# State (one key per miniapp, includes sync metadata)
@dpage_state:{miniappId} → { state, version, lastLocalSave, lastCloudSync }
# Cache (prefixed for easy cleanup)
@dpage:projectId:m:{miniappId}:version → version metadata
@dpage:projectId:m:{miniappId}:c:{name} → component codeNo separate index files – listing uses getAllKeys() with prefix filtering.
Cleanup APIs
// Clear state only
await PersistenceManager.clearMiniapp(miniappId);
// Clear state + cache (complete cleanup)
await PersistenceManager.clearMiniappData(miniappId);
// Clear everything
await PersistenceManager.clearAllData();SDK exports
| Prop | Type | Default | Description |
|---|---|---|---|
initLifecycleListener | Function | - | Enable auto-save on app background (call once at startup) |
useNamedPersistentState | Hook | - | useState-like hook for persistent state |
MiniappStateProvider | Component | - | Context wrapper for miniapps |
PersistenceManager | API | - | Save/restore/clear/sync APIs |
CloudSync | API | - | Built-in DPage cloud sync client |
clearMiniappCache | API | - | Clear cached components for a miniapp |
CloudSyncCallbacks, SyncStatus | Types | - | Cloud sync type definitions |
StateValue, StateSnapshot | Types | - | State type definitions |
References
- SDK docs: STATE_PERSISTENCE.md
- Hooks reference: SDK Hooks