Skip to Content
react-native-kernel is in active development (v0.0.0) — APIs may change before v1.
Docskernel/storage

kernel/storage

SecureStorage (Keychain / Android Keystore) and useOfflineSync(), both backed by native code with zero added dependencies — no MMKV, no AsyncStorage.

import { SecureStorage, useOfflineSync } from 'react-native-kernel/storage';

How the fast store works

useOfflineSync is backed by a small native TurboModule (NSUserDefaults on iOS, SharedPreferences on Android) exposed through synchronous JSI calls — no bridge serialization round trip. That’s what makes it MMKV-fast without adding MMKV: the speed advantage MMKV has over AsyncStorage mostly comes from avoiding the async bridge, not from a custom on-disk format, and TurboModule sync methods get you that for free.

If you’re extending this package yourself: a TurboModule method only becomes truly synchronous (isBlockingSynchronousMethod = true in Codegen) when it has a non-void return type. A void-returning “synchronous” write method silently gets dispatched to a background thread instead — a write immediately followed by a read can then race. Every write method here returns boolean specifically to force the synchronous path.

SecureStorage

import { SecureStorage } from 'react-native-kernel/storage'; await SecureStorage.setItem('access-token', token); const token = await SecureStorage.getItem('access-token'); // string | null await SecureStorage.removeItem('access-token'); await SecureStorage.clear();

Values are stored in the iOS Keychain / encrypted via Android Keystore (AES-GCM, backed by a hardware-derived key). The API is Promise-based even though the underlying calls are synchronous — deliberately, so that a future biometric read-gate (planned for kernel/auth) can slot in without a breaking API change.

useOfflineSync(options)

Local-first read/write with background sync and conflict resolution:

import { useOfflineSync } from 'react-native-kernel/storage'; function ProfileScreen() { const profile = useOfflineSync({ key: 'profile', initialValue: { name: '', bio: '' }, pull: async () => { const { data, updatedAt } = await api.get('/profile'); return { value: data, updatedAt }; }, push: async (record) => { await api.put('/profile', record.value); }, }); return ( <View> <TextInput value={profile.data.name} onChangeText={(name) => profile.setData((prev) => ({ ...prev, name }))} /> {profile.isSyncing && <ActivityIndicator />} {profile.error && <Text>Sync failed: {profile.error.message}</Text>} </View> ); }

Options

OptionTypeDefaultNotes
keystring—Storage key for this record
initialValueT—Used before anything’s been read from disk or the server
pull() => Promise<SyncedRecord<T>>—Fetch the latest server-side record
push(record) => Promise<void>—Push the reconciled record back, when local wins
conflictResolutionsee below'last-write-wins'
syncOnMountbooleantrueCalls sync() once when the hook mounts
syncOnReconnectbooleantrueCalls sync() when connectivity is regained

SyncedRecord<T> is { value: T; updatedAt: number } — every write is timestamped so conflict resolution has something to compare.

Conflict resolution

type ConflictResolution<T> = | 'local' // local always wins, pushes to server | 'remote' // remote always wins, overwrites local | 'last-write-wins' // default — whichever updatedAt is newer wins | ((local: SyncedRecord<T>, remote: SyncedRecord<T>) => T); // custom merge

Return value

FieldTypeNotes
dataTCurrent value — always local-first, updates instantly on setData
setData(value | (prev) => value) => voidPersists to the local KV store immediately, synchronously
isSyncingboolean
isOnlinebooleanLive from kernel/system’s useNetwork()
lastSyncedAtnumber | null
errorError | nullSet when pull/push throws; never throws out of the hook itself
sync() => Promise<void>Manual trigger, in addition to mount/reconnect

setData only writes locally — it does not push to the server on its own. Reconciliation (and the decision to push) happens in sync(), triggered by mount, reconnect, or a manual call. This keeps writes fast and predictable instead of firing a network request on every keystroke.

Next: Roadmap for what’s planned beyond these three modules.

Last updated on