Card Keeper
ReactPWAFirebase

Overview
Card Keeper is a progressive web app for storing physical loyalty, membership, and gift cards in one place. Instead of carrying a wallet full of plastic cards, users scan a card's barcode or QR code with their phone camera and keep a digital copy that can be displayed at checkout. The app is installable on mobile devices and keeps saved cards available offline through Firestore's persistent local cache.
Key Features
- Scan and save cards: The camera-based scanner reads common barcode formats (QR, Code 128/39, Codabar, EAN, UPC, ITF) and stores the captured code value alongside metadata such as title, store, and country.
- Manual card entry: Cards can be added without scanning, covering cases where a code is typed or pasted.
- Card library with controls: A searchable, filterable grid of saved cards supports editing, deletion, and a favourite flag for quick access.
- On-screen barcode display: Saved cards can be rendered back as scannable barcodes or QR codes at the point of sale.
- Account management: Email/password authentication via Firebase Auth, with profile actions such as password change and account deletion.
- Offline-capable storage: Firestore is initialized with a persistent local cache so saved cards remain available without a network connection.
Technical Highlights
- Firebase as backend: Authentication and Firestore provide user-scoped data storage; cards live in a
cardscollection keyed byuserIdwith timestamps, keeping the client simple and serverless. - Redux Toolkit with persistence: UI and form state (page selection, add-card draft, edit mode, card preview, settings, action states) is managed through Redux slices, with
redux-persistpreserving state across reloads. - PWA with Workbox:
vite-plugin-pwagenerates a service worker and web app manifest (standalone display, portrait orientation), enabling installability and offline asset caching. - Camera scanning with format allow-list: The scanner restricts detection to a curated set of barcode formats and provides visual feedback (success/error toasts, vibration, sound) configurable in settings.
- Device-aware UX: Uses
react-device-detectto conditionally trigger haptic feedback on Android, andreact-country-flagto show country context per card. - Offline-first Firestore cache:
initializeFirestoreis configured withpersistentLocalCacheandpersistentSingleTabManager, trading multi-tab concurrency for reliable offline reads.
Challenges & Solutions
- Camera access requires HTTPS: Development uses Vite's
basic-sslplugin to serve over HTTPS locally so the device camera is reachable; production relies on the deployed HTTPS origin. - Scanner lifecycle and state cleanup: After a successful or failed scan, the component closes the scanner and resets the add-card draft, preventing stale state from leaking into the next scan.
Lessons Learned
- Restricting the scanner to a known set of barcode formats reduces false positives compared to enabling all formats, at the cost of needing to extend the list if a new card type appears.
- Combining Firestore's persistent cache with a PWA service worker gives a usable offline experience, but the single-tab manager means concurrent tabs won't share cached state.