---
name: expo-sqlite-local-first
description: "Use when building, refactoring, or reviewing a Expo + SQLite + Drizzle (Local-First Mobile App) project (Expo, React Native, Expo Router, SQLite, Drizzle, TypeScript, NativeWind). Rules for an offline-first Expo SDK 57 app: expo-sqlite with Drizzle migrations bundled in the app, no backend by default, sync as an optional module, EAS Build and Update."
license: MIT
metadata:
  source: https://stackitfast.com/rules/expo-sqlite-local-first
  version: "2026-10-04"
---

# Expo + SQLite + Drizzle (Local-First Mobile App) — Agent Skill

## When to use this skill
- Any task that scaffolds, modifies, refactors, or reviews code in a Expo + SQLite + Drizzle (Local-First Mobile App) codebase.
- Whenever the project depends on Expo, React Native, Expo Router, SQLite, Drizzle, TypeScript, NativeWind.
- Apply these guidelines before proposing architecture, database, or deployment changes.

## Guidelines
# Project Architecture & Guidelines (Expo + SQLite + Drizzle, Local-First)

## 1. System Architecture
- **Framework**: Expo SDK 57 with Expo Router (file-based routes in `app/`). The New Architecture is the only architecture from SDK 55 on.
- **Storage**: `expo-sqlite` on the device is the source of truth, accessed through Drizzle ORM (`drizzle-orm/expo-sqlite`).
- **Migrations**: Generated by `drizzle-kit` with the `expo` driver and bundled into the app; applied on startup with `useMigrations()` before any screen reads data.
- **Settings**: Small key-value state in `expo-sqlite/kv-store` or MMKV, never in the relational schema.
- **Styling**: NativeWind (Tailwind classes) with a small token set.
- **Releases**: EAS Build for store binaries, EAS Update for JavaScript fixes.

## 2. File Layout
- `app/`: Routes only. `_layout.tsx` runs migrations and provides the database; screens import from `src/features/`.
- `src/db/schema.ts`: Every table in one file. `src/db/client.ts`: the `openDatabaseSync()` handle wrapped by `drizzle()`.
- `drizzle/`: Generated migrations plus `migrations.js`; committed, never edited by hand.
- `src/features/<workflow>/`: Queries, hooks, components and tests for one workflow together.
- `src/features/sync/` and `src/features/purchases/`: Only when those features exist, behind their own interface.

## 3. Data Rules
- The app must work with the network off. Any feature that needs a server is optional and degrades to the local path.
- Reads go through Drizzle queries in the feature folder; use `useLiveQuery()` for screens that must update when data changes.
- Every schema change is a new migration generated from `schema.ts`. Never alter tables at runtime.
- Store ids as client-generated UUIDs so records can sync later without remapping keys.
- Export and import (a JSON or CSV file through `expo-file-system` and the share sheet) is the first sync story; it is enough until someone asks for a second device.

## 4. Adding Sync Later
- Sync lives in `src/features/sync/` and talks to the rest of the app only through the local database.
- Prefer an engine with visible queues and a client you can inspect (PowerSync, or a small Hono API with a change log) over opaque magic.
- Accounts exist only when sync is on; a user who never syncs never signs in.

## 5. Coding Standards
- Strict TypeScript, zero `any`. Types come from the Drizzle schema (`typeof items.$inferSelect`), not hand-written interfaces.
- No native modules for storage, files or settings; the Expo SDK covers them.
- Keep screens thin: a screen composes hooks from its feature folder and renders.

## 6. Testing Conventions
- Unit test queries and data logic with Jest (`jest-expo`) against an in-memory SQLite database.
- Maestro flows cover the core workflow with airplane mode on before anything network-related.
- Test a fresh install and an upgrade from the previous release's database, so migrations are exercised on real data.

## 7. Git Workflow & PR Conventions
- Conventional Commits scoped to the feature: `feat(export): CSV export from the history screen`.
- A schema change ships with its generated migration in the same commit.
- Native config changes (plugins in `app.json`) need a new EAS Build; JavaScript-only fixes go out with EAS Update.

## Source
Maintained at https://stackitfast.com/rules/expo-sqlite-local-first — also available as AGENTS.md, CLAUDE.md, and Cursor .mdc.