FFDB Docs

React Native

Persist sessions and connect offline replicas without relying on browser storage APIs.

Bring sessions and replicas to native apps

The React Native package supplies session-storage and native-SQLite adapter contracts without bundling a runtime database.

Applications retain control of Expo/native dependencies while FFDB owns session validation and replica semantics.

Use it for React Native or Expo apps that need durable sessions or offline SQLite replicas.

Requirements for React Native

  • Prerequisite — Version-matched client, sync-client, and react-native SDK packages.
  • Prerequisite — An encrypted async key-value store and SQLite runtime supporting STRICT tables, upsert, and atomic transactions.
  • Required value — AsyncKeyValueStorage, NativeSQLiteDriver, database location, session key, and lifecycle/network triggers.
  • Required value — A user-scope strategy that prevents different identities sharing visible cached rows.

Install the native integration

React Native uses exact-version @ffdb/client, @ffdb/sync-client, and @ffdb/react-native npm packages. The matching tag provides signed offline tarballs. The integration supplies contracts and adapters; your app still chooses its SecureStore-like and SQLite implementations.

Terminalsh
VERSION=0.3.0
npm install --save-exact "@ffdb/client@$VERSION" \
  "@ffdb/sync-client@$VERSION" "@ffdb/react-native@$VERSION"

Runtime adapters

@ffdb/react-native supplies contracts for asynchronous session storage and native SQLite replicas. It does not bundle a direct Expo SQLite or SecureStore implementation; wrap the runtime APIs your application already owns.

  • AsyncKeyValueStorage implements async getItem, setItem, and removeItem; inject a SecureStore-like encrypted implementation for refresh tokens.
  • NativeSQLiteDriver implements parameterized execute plus a transaction callback that keeps every callback statement on the same atomic transaction.
  • The SQLite runtime must support STRICT tables and ON CONFLICT ... DO UPDATE, which the replica uses for metadata, rows, pending mutations, and rejections.
  • NativeSQLiteReplica owns reserved __ffdb_client_* tables in that local database.
  • @ffdb/client owns HTTP and @ffdb/sync-client owns snapshot/push/pull orchestration.
  • getRow and listRows return decoded local records without exposing the private SQLite connection.
native-ffdb.tsts
import { FFDBClient } from "@ffdb/client";
import {
  NativeSQLiteReplica,
  ReactNativeSessionStore,
  type AsyncKeyValueStorage,
  type NativeSQLiteDriver,
} from "@ffdb/react-native";
import { OfflineSyncClient } from "@ffdb/sync-client";

declare const secureStorage: AsyncKeyValueStorage;
declare const sqliteDriver: NativeSQLiteDriver;

const ffdb = new FFDBClient({
  baseUrl: "https://data.example.com",
  projectId: "your-project-id",
  sessionStore: new ReactNativeSessionStore(secureStorage),
});

const replica = new NativeSQLiteReplica(sqliteDriver);
await replica.initialize();
export const sync = new OfflineSyncClient(ffdb, replica);

export const readDraft = (id: string) => sync.getRow("drafts", id);
export const listDrafts = () => sync.listRows("drafts");

Schedule mobile sync

The package intentionally does not import NetInfo, AppState, Expo SQLite, or SecureStore. The application owns those dependencies and decides when background network work is permitted.

  • Wire onNetworkAvailable to your NetInfo-equivalent online transition.
  • Wire onApplicationActive to the React Native AppState-equivalent active transition.
  • Do not run a tight polling loop while backgrounded.
  • A transient initialize() failure is retryable; the replica clears its failed initialization promise before the next operation.
mobile-lifecycle.tsts
import type { SyncMutation } from "@ffdb/client";

export async function onNetworkAvailable(): Promise<void> {
  await sync.sync();
}

export async function onApplicationActive(): Promise<void> {
  await sync.sync();
}

export function onUserMutation(mutation: SyncMutation): Promise<void> {
  return sync.mutate(mutation);
}

React Native workflow

  • 1. Wrap the runtime storage in AsyncKeyValueStorage.
  • 2. Wrap SQLite execute and transaction APIs in NativeSQLiteDriver.
  • 3. Construct ReactNativeSessionStore and NativeSQLiteReplica.
  • 4. Initialize the replica and construct OfflineSyncClient.
  • 5. Wire active/online events to bounded sync attempts.

Verify react native

Valid sessions, rows, cursors, and queued mutations persist across app restart without browser APIs.

Troubleshoot react native

  • Persisted session JSON is invalid — the store removes it and requires sign-in.
  • Initialization fails transiently — fix the runtime cause and retry; the adapter clears the failed promise.

Continue from React Native

  • Test cold-start and account-switch behavior.
  • Implement rejected-mutation UI.