β Live Demo (with real-time WebSocket)
If the WebSocket server (Railway) is down due to free-tier limits, the app will gracefully fall back to HTTP-only updates. You can still browse all pages and patterns.
A full-stack personal finance tracker running in production on Vercel, backed by a real Neon Postgres database and a dedicated WebSocket server on Railway. Built with Next.js 15, React 19, and TypeScript.
What makes this different: This is an actual production deploymentβreal database, real WebSocket sync across devices, Lighthouse 97 on the heaviest page. Virtual scrolling renders only ~15 DOM nodes from 2000+ records. Every performance number in this README is measured, reproducible, and linked to evidence.
| Feature | Implementation | Impact |
|---|---|---|
| Virtual Scrolling | @tanstack/react-virtual | Only rendering ~15 visible rows from 1000+ item datasets (~98% DOM reduction), maintaining ~62 FPS during scroll β verified via DevTools Performance and Chrome Frame Rendering |
| Real-Time Sync | Native WebSocket + BroadcastChannel | <100ms cross-device updates, 80% connection efficiency |
| Optimistic Updates | React Query mutations | <16ms UI response, graceful rollback |
| Infinite Scroll | IntersectionObserver + cursor pagination | Smooth loading, no layout shift |
| State Management | React Query + query key factory | Zero prop drilling, intelligent caching |
β Performance Metrics | Virtual List Deep Dive | WebSocket Architecture | Optimistic Updates
| Metric | Value |
|---|---|
| Lighthouse Performance | 97 |
| First Contentful Paint (FCP) | 0.6s |
| Largest Contentful Paint (LCP) | 0.6s |
| Scroll FPS | ~62 FPS |
| DOM nodes rendered | ~15 of 2000+ items (~98% reduction) |
Measured on production deployment: money-tracking-react-web.vercel.app with real-time WebSocket sync active.
Screenshots:
- Lighthouse (Throttling x4, 3G):
- DOM nodes for virtual list (15/2000+):
- FPS & GPU:
Real-time balance tracking with week/month comparisons, top expense categories, and recent transactions. Infinite scroll powered by IntersectionObserver with fallback pagination.
Tech: React Query for CSR, Recharts for visualizations, optimistic balance updates
Organize transactions by categories with drag-and-drop. Handles 500+ transactions per column smoothly via virtualization.
Key Patterns:
- Virtual scrolling renders only visible cards (~20 DOM nodes for 500 items)
- Optimistic drag-and-drop with instant UI feedback + rollback on error
- Real-time sync via WebSocketβchanges from one device appear on others instantly
- Multi-tab optimization via leader election (1 WebSocket for N tabs)
Tech Stack:
@tanstack/react-virtualfor virtualization@dnd-kitfor accessible drag-and-drop- Native WebSocket with auto-reconnect
- React Query cache manipulation for zero-refetch updates
Month selector, grouped timeline view, add/edit/delete with modals, monthly reports with expense distribution.
Tech: useInfiniteQuery with cursor pagination, modal system with Radix UI, Recharts pie charts
Public monthly reports at /reports/monthly/[month] with ISR (24h revalidation).
Tech: Next.js ISR, server components for data fetching, SEO-optimized
Mock authentication with JWT tokens in httpOnly cookies, middleware protection for private routes.
Tech: Server Actions for auth flow, jose for JWT signing/verification, middleware route guards
- Query key factory for hierarchical invalidation
- Optimistic mutations with 3-phase pattern (onMutate β onError β onSuccess)
- Smart prefetching for adjacent data
- Stale-while-revalidate strategy for instant navigation
β Full React Query Architecture
- Auto-reconnect with exponential backoff (1s β 2s β 4s β 8s β 16s)
- Leader election via BroadcastChannel (multi-tab efficiency)
- Version-based conflict resolution
- Event deduplication to prevent double updates
β WebSocket Implementation Details
- Variable height support (cards with notes are taller)
- GPU-accelerated positioning (
transformvstop) - Scroll position stability during real-time updates
- Infinite scroll integration at 80% threshold
Core:
- Next.js 15 (App Router)
- React 19
- TypeScript (strict mode)
- Tailwind CSS
State & Data:
- @tanstack/react-query (server state)
- @tanstack/react-virtual (list virtualization)
- Native WebSocket (real-time)
UI Libraries:
- shadcn/ui + Radix UI (accessible components)
- @dnd-kit (drag-and-drop)
- Recharts (data visualization)
- lucide-react (icons)
Dev Tools:
- Vitest + Testing Library (testing)
- ESLint + TypeScript (code quality)
- pnpm (package management)
1. Install dependencies
pnpm install2. Configure environment variables
Create a .env.local file in the project root:
# Neon Postgres β get your connection string at https://neon.tech
DATABASE_URL="postgresql://user:password@host/dbname?sslmode=require"
# Optional: direct (non-pooled) connection for Prisma CLI migrations
# DIRECT_URL="postgresql://user:password@host/dbname?sslmode=require"
# JWT signing secret β change this to a random string in production
AUTH_SECRET="your-secret-here"3. Set up the database
# Push the schema to your Neon database (first-time setup or dev)
pnpm db:push
# Or run migrations (production-style)
pnpm db:migrate
# Seed with sample transactions (optional)
pnpm db:seed
# Open Prisma Studio to browse your data (optional)
pnpm db:studio4. Start the app
# Terminal 1 β Next.js dev server
pnpm dev
# Terminal 2 β WebSocket mock server (for real-time sync)
pnpm ws-serverTest Real-Time Sync:
- Open the app in two browser tabs
- Drag a transaction in Tab 1
- Watch it update in Tab 2 instantly β¨
Other commands:
pnpm test # Run tests
pnpm test:coverage # Coverage report
pnpm typecheck # TypeScript check
pnpm lint # ESLintsrc/
βββ app/ # Next.js App Router
β βββ login/ # Auth route
β βββ dashboard/ # Dashboard page
β βββ transactions/ # Transactions + Kanban sub-routes
β β βββ kanban/
β βββ reports/monthly/[month]/ # Public ISR reports
β βββ api/ # Route Handlers (transactions, balance, summaryβ¦)
βββ features/ # Feature-based modules (co-located API, components, hooks)
β βββ dashboard/
β βββ transactions/
β βββ kanban/
βββ components/ui/ # Shared UI primitives (shadcn/ui)
βββ lib/
β βββ query-keys.ts # React Query key factory
β βββ websocket/ # WebSocket client + mock server
β βββ sync/ # BroadcastChannel leader election
βββ store/ # Redux store (UI state)
βββ hooks/ # Shared hooks
βββ providers/ # React Query, Auth context
docs/ # Architecture documentation
metrics/ # Performance tracking
prisma/ # Schema + seed
- React Query Architecture - Query keys, caching, optimistic updates
- Virtualized List - Performance optimization for large lists
- WebSocket Sync - Real-time synchronization architecture
- Optimistic Updates - Instant UI with rollback patterns
- Performance Metrics - Benchmarks and targets
This project demonstrates patterns commonly used in production applications:
Beginner-Friendly:
- Next.js App Router with Server/Client Components
- React Query for server state management
- Infinite scroll with IntersectionObserver
Intermediate:
- Virtual scrolling for performance
- Optimistic updates with rollback
- Query key factory pattern
Advanced:
- WebSocket with leader election
- Multi-tab synchronization via BroadcastChannel
- Version-based conflict resolution
- Concurrent mutation handling
# Run all tests
pnpm test
# Watch mode
pnpm test --watch
# Coverage report
pnpm test:coverageTests include:
- Unit tests for utilities
- Component tests for key features
- Mocked API calls
- API: Route Handlers backed by Neon Postgres (
/api/transactions,/api/balance,/api/summary,/api/expenses/top) - Authentication: JWT in httpOnly cookies via
jose; mock user store (auth patterns are production-grade, user management is intentionally minimal) - WebSocket Server: Deployed on Railway β real-time sync is fully functional on the live demo. Run
pnpm ws-serverfor local development.
This is a personal learning project, but feel free to:
- Open issues for bugs or suggestions
- Fork and experiment with patterns
- Reference code for your own projects
Live on Vercel β β Browse the code to see production patterns applied to a real, deployed application. If WebSocket real-time sync is unavailable (Railway free tier expired), all features still work over HTTP.