Skip to content
Skip to content
Goodspeed

BUILT INTO EVERY GOODSPEED APP

Offline-First Sync with Write Queue

Writes made while offline are queued in AsyncStorage with TTL and automatically replayed in order when connectivity returns, so users never lose data or see failed-action errors on a flaky connection.

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

WHY IT MATTERS

Most mobile app projects spend weeks plumbing the same infrastructure before writing a single line of product code. Offline-First Sync with Write Queue 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-first sync with write queue 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.offlineSync.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/offline.ts in the gas-template repository. This is the code your generated app gets, not pseudocode, not a description of intent.

// lib/offline.ts -- write queue: enqueue offline, flush on reconnect

export async function queueMutation(
  payload: Omit<MutationPayload, 'retries'>
): Promise<void> {
  const raw = await readQueue();
  let queue: MutationPayload[];
  try { queue = raw ? JSON.parse(raw) : []; }
  catch { queue = []; captureEvent('offline_queue_corrupted', { action: 'queue_mutation' }); }
  queue.push({ ...payload, retries: 0 });
  if (queue.length > MAX_QUEUE_SIZE) queue = queue.slice(-MAX_QUEUE_SIZE);
  await writeQueue(JSON.stringify(queue));
  addBreadcrumb('offline', 'Mutation queued', { endpoint: payload.endpoint });
}

export async function flushQueue(
  executor: (p: MutationPayload) => Promise<void>
): Promise<FlushResult> {
  const result: FlushResult = { executed: 0, failed: 0, dropped: 0 };
  if (flushing) return result;
  flushing = true;
  try {
    const queue: MutationPayload[] = JSON.parse(await readQueue() ?? '[]');
    const remaining: MutationPayload[] = [];
    for (const item of queue) {
      try { await executor(item); result.executed++; }
      catch {
        if (item.retries < 5) { remaining.push({ ...item, retries: item.retries + 1 }); result.failed++; }
        else { result.dropped++; captureEvent('offline_mutation_dropped', { endpoint: item.endpoint }); }
      }
    }
    await writeQueue(JSON.stringify(remaining));
    return result;
  } finally { flushing = false; }
}

Source: goodspeed-apps/gas-template lib/offline.ts

HONEST LIMITS

When Offline-First Sync with Write Queue is the wrong choice

Not appropriate for real-time collaborative apps where stale offline writes would cause conflicts; use the realtime-presence and optimistic-mutations pattern instead.

Tier: Common · Config-toggled

  1. Evaluate your use case

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

  2. Audit the config

    The `features.offlineSync.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

Every generated Goodspeed app includes offline-first sync with write queue. Browse the ideas catalog to see apps across all categories that ship with this feature wired in.

CAPABILITIES

Offline-First Sync with Write Queue capability breakdown

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

ItemDescriptionStrength
Queue storagePending mutations are written to AsyncStorage under a per-app-slug key. The queue survives app restarts and is flushed on the next successful reconnect.AsyncStorage (persistent)
Encryption optionWhen gasConfig.features.offlineSync.encrypted is true, the queue is stored in expo-secure-store (iOS Keychain / Android Keystore) with automatic fallback for payloads over 2KB.SecureStore (optional)
Flush triggerflushQueue() fires on reconnect (NetInfo) and can be called manually. A module-level lock prevents concurrent flush runs from double-processing items.On reconnect + manual
Retry policyEach mutation carries a retry counter. Transient failures keep the item in queue. After 5 retries the item is dropped and offline_mutation_dropped fires.5-retry limit
Overflow evictionCapped at 100 items (MAX_QUEUE_SIZE). When exceeded, oldest items are evicted and offline_queue_overflow fires so operators can monitor pressure.100-item cap

GET IT BUILT INTO YOUR APP

Score your idea and get offline-first sync with write queue wired in from day one