Skip to content
Skip to content
Goodspeed

BUILT INTO EVERY GOODSPEED APP

Offline Sync

An offline queue captures writes when the device has no connectivity and replays them automatically when the connection returns. No data is silently lost. The sync status is surfaced via an OfflineBanner component.

  • Tier: Common
  • Status: Config-toggled
  • Config: features.offlineMode.enabled

WHY IT MATTERS

Most mobile app projects spend weeks plumbing the same infrastructure before writing a single line of product code. Offline Sync is one of those cross-cutting concerns that every app eventually needs but almost none get right the first time. Permissions are handled incorrectly, tokens expire silently, or the feature breaks after an OS update nobody tested against.

Goodspeed solves this by shipping offline sync as a production-grade, tested implementation inside every generated app. The code follows the patterns in the GAS template - the same 246-feature catalog that powers every app we build. Controlled by `features.offlineMode.enabled` in gas.config.ts. You own the code from day one, can read every line, and can hire any React Native developer to extend it. The build pipeline verifies the feature compiles and routes resolve before the app lands in your repository, so you are not the one catching the integration error at 2 am before launch.

HOW IT IS WIRED

Real code from the GAS template

The excerpt below is lifted verbatim from lib/background-tasks.ts in the gas-template repository. This is the code your generated app gets, not pseudocode, not a description of intent.

// Offline queue pattern — writes captured locally, replayed on reconnect
import NetInfo from '@react-native-community/netinfo';

export function registerOfflineQueueProcessor(): () => void {
  const unsubscribe = NetInfo.addEventListener(async (state) => {
    if (!state.isConnected) return;
    const pending = await localDb.getAll('offline_queue');
    for (const op of pending) {
      try {
        await replayOperation(op);
        await localDb.delete('offline_queue', op.id);
      } catch (e) {
        if (isFatalError(e)) await localDb.delete('offline_queue', op.id);
        // Transient errors stay in queue for next reconnect
      }
    }
  });
  return unsubscribe;
}

export async function enqueueOfflineWrite(
  table: string,
  payload: Record<string, unknown>,
): Promise<void> {
  await localDb.insert('offline_queue', {
    id: crypto.randomUUID(), table, payload,
    created_at: new Date().toISOString(),
  });
}

Source: goodspeed-apps/gas-template lib/background-tasks.ts

HONEST LIMITS

When Offline Sync is the wrong choice

Apps with real-time multi-user collaboration (shared whiteboards, co-editing) should not rely on the offline queue pattern alone. Conflict resolution across concurrent offline edits requires operational transforms or CRDTs that the template does not provide.

Tier: Common · Config-toggled

  1. Evaluate your use case

    Check whether offline sync aligns with your target audience, platform constraints, and regulatory environment before enabling it.

  2. Audit the config

    The `features.offlineMode.enabled` flag controls this feature. Set it to false in gas.config.ts to disable the feature entirely with no residual code paths.

  3. Seek alternatives

    If the built-in implementation does not fit, the generated codebase is standard React Native + Expo code. Any library in the Expo ecosystem can replace the default.

APPS USING THIS FEATURE

Apps built with Offline Sync

These apps were generated by Goodspeed and use offline sync as a core part of their experience. Each link goes to the full app marketing page.

CAPABILITIES

Offline Sync capability breakdown

Concrete dimensions of what the built-in offline sync implementation covers. These reflect the actual template code, not a marketing summary.

ItemDescriptionStrength
Queue storagePending writes are stored in expo-sqlite on-device. The queue persists across app restarts.SQLite (local)
Replay triggerNetInfo.addEventListener fires the replay processor the moment connectivity is restored.On reconnect
Error handlingFatal errors (4xx) are dequeued. Transient errors stay queued for the next reconnect attempt.Retry on transient
BannerOfflineBanner renders a persistent top-of-screen indicator while the device is offline.Built in

GET IT BUILT INTO YOUR APP

Score your idea and get offline sync wired in from day one