| ← All Docs Performance

Performance

Verified Last updated: September 2026

Performance profile of AquaDealers: caching architecture, code splitting strategy, known bottlenecks, and what's working well.

Caching Strategy

Two-Layer Cache: TanStack React Query + IndexedDB

All API data flows through TanStack React Query with persistence to IndexedDB via idb-keyval. This provides both in-memory caching and cross-session persistence.

SettingValueEffect
staleTime5 minutesData considered fresh for 5 min; no refetch during this window
gcTime24 hoursUnused cache entries garbage-collected after 24 hours
networkModeofflineFirstServe from cache immediately, refetch in background when online
PersistenceIndexedDB (idb-keyval)Cache survives browser restarts; restored on app launch
Offline support: The offlineFirst network mode combined with IndexedDB persistence means the app remains functional (read-only) even without network connectivity. Mutations queue and replay when online.

Code Splitting

64
Lazy-loaded pages
PWA
Service worker precaching

Implementation

Known Performance Issues

SeverityIssueLocationImpact
High Monthly report unbounded fetch /reports page 10+ queries with no row limits or pagination. Fetches ALL bills, payments, and expenses for the selected period. Will cause timeouts and memory issues for high-volume dealers (1000+ bills/month).
High getDuesSummary fetches 10,000 farmer rows Dues summary calculation Pulls up to 10,000 farmer rows client-side to calculate dues summary. All data transferred over the network and processed in the browser. Should be a server-side aggregation.
High Bill PDF html2canvas freezes mobile PDF generation (Bill details) html2canvas renders the bill DOM to a canvas element, which freezes the main thread for 3–5 seconds on mobile devices. UI is completely unresponsive during this time.
Medium Dashboard 15+ parallel queries on cold load Dashboard page Cold load fires 15+ Supabase queries simultaneously. On slow connections, this can saturate the connection pool and cause waterfall delays. Subsequent loads are fast due to caching.
Medium 35+ queries use select('*') Various service files Selecting all columns transfers unnecessary data over the network. Impact compounds on large tables (bills, bill_items, inventory_lots). Should select only needed columns.
Medium Missing loading="lazy" on images Product and farmer images All images load eagerly, increasing initial page weight. Particularly noticeable on product listing and farmer list pages with many entries.
Low Cashbook no pagination Cashbook page Loads all cashbook entries for the selected period without pagination. Acceptable for typical usage but will degrade with very high transaction volumes.

What's Working Well

React Query + IndexedDB

Solid offline experience. Cache persists across sessions. Background refetches keep data fresh without blocking UI. offlineFirst mode means instant renders from cache.

All Routes Lazy-Loaded

64 pages all use React.lazy(). Initial bundle is small. Only the current page's code is loaded. Combined with service worker precaching for fast subsequent visits.

No Realtime Subscriptions

The app uses polling (via React Query's refetch intervals) instead of Supabase Realtime. This avoids WebSocket connection overhead and simplifies the data layer. Acceptable because AquaDealers is primarily single-user (one dealer, one device at a time).

Server-Side Search

Large dataset searches (farmers, products) use server-side search RPCs rather than loading all data client-side. This keeps search responsive even with thousands of records.

Virtualized Dues Page

The Dues page uses useWindowVirtualizer (TanStack Virtual) to render only visible farmer rows. Handles thousands of farmers without DOM bloat.

Daily Book Prefetching

The Daily Book prefetches adjacent dates (previous day, next day) so date navigation feels instant. Data is ready before the user taps the arrow.