AI Projects/PairList

Project Deep Dive

PairList

PairList

Shared shopping lists, built right

CompleteReactViteTailwind CSSSupabaseDexie.jsPWAOpen PairList — Free to Use ↗

The Problem

Shared shopping lists are a solved problem — except they're not. Every existing solution fails in at least one of the same three ways: they require a live connection to register a tap, they don't handle two people editing simultaneously without data loss, or they feel like a note-taking app with a share button stapled on.

The specific failure mode that motivated this project: you're in a grocery store with bad signal, you check off an item, the app silently ignores it, and you get home having bought things twice or not at all. That's a UX contract violation — the app accepted your input and lost it.

PairList was built around a single constraint: every edit must succeed, regardless of connectivity. Everything else — the realtime sync, the RLS isolation, the PWA shell — follows from that.

Screenshots

List view
List view
Items List
Items List
Offline mode
Offline mode

Architecture Highlights

Offline-first

Works with no connection — edits queue locally and sync when back online.

Outbox pattern

Conflict-free sync via a local outbox queue — no data loss if the connection drops mid-edit.

Supabase Realtime

Changes propagate instantly to all connected devices in the same household.

Row-level security

Strict per-household data isolation enforced at the database layer.

PWA

Installable on iOS and Android — feels native, no app store required.

Swipe gestures

Mobile-native swipe-to-complete on every item — designed for one-handed grocery use.

Design Decisions

Why offline-first?

Grocery stores have terrible signal. Most shared list apps silently fail when offline — you tap, nothing happens, and you leave the store guessing. The architecture guarantee here is that every edit succeeds locally first, always.

Why Dexie.js + outbox instead of just Supabase?

Supabase is the source of truth, but the client can't depend on a live connection. Dexie.js maintains a local IndexedDB store that mirrors the list state. The outbox queue serializes pending writes and replays them in order when connectivity returns — so two people editing offline don't create conflicts.

Why Supabase over Firebase?

Postgres row-level security gives per-household isolation without custom middleware. Supabase Realtime handles the live-sync layer cleanly. And it's open source with a self-hostable path if needed.

Why a PWA instead of a native app?

Zero install friction. The whole point is that both people in a household actually use it. A PWA installs in one tap from the browser — no App Store, no Play Store, no version mismatch.

What I Learned

Offline-first sounds simple until you implement it. The hard part isn't the local store — it's the sync. Naive approaches (last-write-wins, timestamp comparison) break silently when clocks drift across devices or when edits are batched. The outbox queue pattern forces you to think about writes as a serial log rather than a state snapshot, which makes conflict resolution tractable.

Supabase Realtime is genuinely good for this use case — the latency on live updates is low enough that it feels instant across devices on the same network. Row-level security replaced what would have been a layer of custom auth middleware, which simplified the backend significantly.

PWA installation UX is still rough on iOS. Safari's "Add to Home Screen" prompt is buried and non-obvious — the install rate on iOS is meaningfully lower than Android for the same user base. That's a platform constraint, not an app constraint, but it's worth knowing going in.