DRTYLABS
WorkServicesBlogAboutContact
contact@drtylabs.ca
Back to Blog
AI IntegrationMar 15, 202610 min read

Offline-First Mobile Apps with Expo, Drizzle, and SQLite: A 2026 Pattern

Offline Is Not a Bug, It Is a Feature

Most apps treat offline as an error state. The user is offline, so we show a "no connection" message and disable functionality. That works for consumer apps where being offline is rare. It does not work for the apps people actually use to do their jobs. Warehouse staff scanning inventory in steel-roofed buildings. Field technicians working in basements. Delivery drivers in parking garages. These users are offline frequently, and the app needs to keep working anyway.

Offline-first flips the architecture. The local device is the source of truth from the user's perspective. Every operation completes immediately against local storage. Sync to the server happens in the background, asynchronously, with conflict resolution when the network returns. The user never has to think about whether they are online. They just use the app.

The Stack — Expo, Drizzle, SQLite

In 2026, the cleanest offline-first stack on the JavaScript side is Expo + Drizzle ORM + SQLite. Expo gives you the cross-platform shell, the camera and biometric APIs, and the build pipeline. Drizzle gives you a type-safe ORM that runs identically on the server (Postgres) and the device (SQLite) — same query API, same type system, different drivers underneath. SQLite gives you a real database in your pocket, fast enough that local queries feel instant.

The reason this stack matters is the type contract. The same Drizzle schemas that define your Postgres tables on the server can describe your SQLite tables on the device. The same TypeScript types for an Order or a Product flow through the API, the dashboard, and the mobile app. When the schema evolves, you migrate both sides. The discipline of keeping them in sync is the architecture's main cost — and the main payoff is that bugs around data shape simply do not happen.

The Pending Mutations Queue

The core data structure of an offline-first app is the pendingMutations table. Every write the user performs — creating an order, updating inventory, marking a task complete — is committed to local SQLite immediately AND pushed to a pendingMutations queue with a unique ID, a timestamp, the operation type, and the payload. The user sees their write succeed instantly because it did, locally.

In the background, a sync worker drains the queue. It connects to the server, applies each mutation in order, and on success removes the entry from the local queue. On failure, it retries with exponential backoff. On conflict — the server has a newer version of the same record — it surfaces the conflict to a UI affordance the user can resolve. The queue is durable, so if the app is killed, the queue persists. When the user opens the app three days later in a coverage area, the queue picks up exactly where it left off.

Conflict Resolution UI That Doesn't Suck

Most teams ship offline sync with last-write-wins conflict resolution. The user makes a change. The server has a newer change. The user's change is silently overwritten. This is fine until it is not — until the user has spent ten minutes capturing inventory data that disappears. Then it is a trust-destroying disaster.

The pattern we ship is explicit conflict surfacing. When a sync attempt finds the server has a different version of the record, the conflict goes into a "conflicts" tab in the app. The user sees both versions side by side, can pick which fields to keep from which, and can make an informed decision. For 95 percent of records, conflicts are rare. For the 5 percent where they happen, the user actually gets to resolve them rather than having data silently disappear. The UI is more work to build. The trust it earns is worth the work.

Sync Status Visibility

Users need to see the state of the sync queue. Silent background sync sounds clean, but the moment a sync starts failing — bad credentials, server outage, version mismatch — the user is shipping data into a black hole and does not know it. The fix: a small, persistent sync indicator in the UI that shows queued / syncing / conflict / failed counts.

The status is computed from the pendingMutations table on every render. A retry-all button is exposed when there are failed mutations. Users learn to glance at the indicator the same way they glance at battery percentage. We have shipped apps where warehouse managers explicitly check sync status before ending a shift. That is the right level of visibility — present, informative, never alarming when things are normal.

The Camera, Biometrics, and Native Bridges

Offline-first is not just about data. It is about the rest of the device's capabilities. The camera for barcode scanning. Biometric auth for app-level security. Push notifications for sync alerts. These are native APIs, and Expo's coverage of them in 2026 is genuinely excellent.

For barcode scanning, expo-camera handles EAN-13, UPC, Code128, QR, PDF417, ITF14, and Codabar in a single unified API. For biometrics, expo-local-authentication wraps Face ID, Touch ID, and Android biometrics. For secure storage of credentials, expo-secure-store maps to Keychain on iOS and EncryptedSharedPreferences on Android. The stack lets you build apps that genuinely feel native — fast, integrated with the device, properly secured — without leaving the JavaScript ecosystem. That was not true two years ago. It is now.

When Not to Build Offline-First

Offline-first is real engineering work. It is not the right answer for every app. Consumer apps where users are almost always online. Apps where the data is purely server-side and there is nothing meaningful to do offline. Apps where the merge complexity of local writes is genuinely not worth the engineering investment.

The litmus test: would the app's value to the user collapse if they lost connectivity for 30 minutes? If yes, offline-first is justified. If no, build a simpler architecture and reach for offline only when usage data shows it matters. We have seen teams build elaborate offline systems for products where 99.5 percent of sessions are online — engineering work that nobody benefits from. Pick this pattern when the user genuinely needs it. When they do, it is the difference between a tool they tolerate and a tool they trust.

Ready to put this into action?

We build the digital infrastructure that turns strategy into revenue. Let's talk about what DRTYLABS can do for your business.

Get in Touch