Offline & Caching
What the Expo scaffold does — and honestly, does not — do when the network drops. Grit Mobile is an online-first, API-backed app with React Query caching and persistent auth. It is not an offline-first sync engine, and this page is precise about the difference.
The short version
The Expo scaffold does not ship a local-mirror / outbox sync engine — that is a Grit Desktop feature (local SQLite mirror + versioned outbox). On mobile, data lives on the Go API. What you get out of the box is React Query's in-memory cache (so recently viewed data stays instant and survives brief blips), fast-fail networking, and token persistence across restarts. Full offline writes are something you add deliberately.
React Query caching
Every generated hook is a React Query query, and the scaffold configures a shared client in lib/query-client.ts. Two defaults shape the offline-ish behaviour: a five-minute staleTime (data is considered fresh for five minutes, so re-visiting a screen renders cached data immediately instead of showing a spinner) and one retry on failure.
import { QueryClient } from "@tanstack/react-query";export const queryClient = new QueryClient({defaultOptions: {queries: {staleTime: 1000 * 60 * 5, // fresh for 5 minutesretry: 1, // one retry before surfacing an error},},});
Because the cache is in memory, it is warm for the life of the running app — navigate away and back and the data is there — but it is cleared when the app process is killed. To make it survive restarts you'd add a persister; see “Going further” below.
What actually persists
One thing genuinely survives an app restart: the auth session. Tokens are written to expo-secure-store (the iOS Keychain / Android Keystore), so users stay logged in between launches even with no network at open time.
| State | Where it lives | Survives restart? |
|---|---|---|
| Access / refresh tokens | SecureStore (Keychain / Keystore) | Yes — encrypted |
| Fetched resource data | React Query cache (memory) | No — cleared on kill |
| Theme preference | SecureStore | Yes |
| In-flight mutations | None — sent live to the API | No |
Networking that fails fast
A flaky connection shouldn't freeze the UI. Grit's API client wraps every request in a 15-second timeout, so a request against an unreachable API aborts instead of hanging — that timeout is specifically what keeps the splash screen from getting stuck at launch. The client also handles token refresh: on a 401 it calls /auth/refresh once and retries, and unsafe methods carry a stable idempotency key so a retried write can't double-apply.
const REQUEST_TIMEOUT_MS = 15000; // fail fast instead of hanging for minutesasync function fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {const controller = new AbortController();const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);try {return await fetch(url, { ...init, signal: controller.signal });} finally {clearTimeout(timer);}}
What happens when the network drops
Screen mounts│├─ Data cached & fresh (< 5 min) → renders instantly from cache├─ Data cached but stale → shows cache, refetches in background└─ Nothing cached → shows loading, then an error afterthe retry + 15s timeoutWrites (create / update / delete)└─ Sent live to the API. Offline → the mutation errors; the screensurfaces it (e.g. an Alert). There is no local queue that replaysthe write when connectivity returns.
Going further: true offline support
If your product genuinely needs to work with no connection — capture data on a plane, in a warehouse, in the field — you can layer it on with standard React Query + Expo pieces. Grit doesn't generate this, but it composes cleanly with what's there:
- Persist the query cache. Add
@tanstack/react-query-persist-clientwith an AsyncStorage persister so cached reads survive app restarts. - Detect connectivity. Use
@react-native-community/netinfowith React Query'sonlineManagerso queries pause offline and resume on reconnect. - Queue writes. React Query's mutation persistence /
resumePausedMutations()lets you replay mutations made while offline once the device is back online. - Or go desktop. If offline-first is central, Grit Desktop ships a full sync engine (local SQLite mirror + outbox) today — see Building an Offline-First Desktop App.
