A fully functional offline-first note-taking Progressive Web App built with Next.js, TypeScript, and Supabase.
- ✅ Create, Read, Update, Delete notes
- ✅ Title (max 100 characters) and content (max 5000 characters)
- ✅ Timestamps for created and modified dates
- ✅ Responsive design (mobile and desktop)
- ✅ Works completely offline after initial load
- ✅ IndexedDB for persistent local storage
- ✅ Service Worker with caching strategies
- ✅ Background Sync API integration
- ✅ Automatic synchronization when online
- ✅ Per-note sync status indicators (synced, syncing, pending, failed)
- ✅ Automatic conflict resolution (Last Write Wins strategy)
- ✅ Queue system for failed operations
- ✅ Background sync registration
- ✅ Sidebar layout with shadcn/ui components
- ✅ Grid and List view toggle
- ✅ Real-time online/offline status
- ✅ Toast notifications for user feedback
- ✅ Confirmation dialog for deletions
- ✅ Character count indicators
- Framework: Next.js 14
- Language: TypeScript
- UI Components: shadcn/ui
- Styling: Tailwind CSS
- State Management: Zustand
- Data Fetching: TanStack Query (React Query)
- Database: Supabase
- Offline Storage: IndexedDB (via idb)
- Service Worker: Custom implementation
src/
├── app/
│ ├── layout.tsx # Root layout with metadata
│ ├── page.tsx # Main notes page
│ ├── providers.tsx # Query & toast providers
│ └── globals.css # Global styles
├── components/
│ ├── layout/
│ │ ├── Header.tsx # Header with view toggle
│ │ └── Sidebar.tsx # Sidebar navigation
│ └── ui/ # shadcn/ui components
├── modules/
│ └── notes/
│ ├── components/ # Note-specific components
│ │ ├── NoteCard.tsx
│ │ ├── NotesList.tsx
│ │ ├── NoteFormDialog.tsx
│ │ └── DeleteDialog.tsx
│ ├── queries/ # React Query hooks
│ ├── mutations/ # Mutation hooks
│ ├── utils/ # Helper functions
│ └── types.ts # TypeScript interfaces
└── lib/
├── api/ # Supabase API client
├── db/ # IndexedDB operations
├── sync/ # Sync logic & conflict resolution
└── store/ # Zustand stores
This application uses the Last Write Wins (LWW) strategy for conflict resolution:
- When syncing, the system compares the
modified_attimestamps - The note with the most recent timestamp takes precedence
- Local changes are preserved if they're newer than server data
- Server data overwrites local data if it's newer
- Simple and predictable behavior
- Works well for single-user scenarios
- Minimal complexity in implementation
- Aligns with user expectations
npm installCreate a .env.local file with:
NEXT_PUBLIC_SUPABASE_URL=https://test.supabase.co/rest/v1
NEXT_PUBLIC_SUPABASE_ANON_KEY=test-key-123
NEXT_PUBLIC_USER_ID=test-user-456npm run devnpm run build
npm run start- Vercel: Recommended (Zero-config deployment)
- Netlify: Fully supported
- Cloudflare Pages: Supported
- Open Chrome DevTools (F12)
- Go to Application tab
- Click Service Workers
- Check "Offline" checkbox
- Test all CRUD operations
- Uncheck "Offline" to see sync in action
- Strategy: Cache First
- Assets: HTML, CSS, JS, icons, manifest
- Fallback: Network if cache miss
- Strategy: Network First
- Fallback: Serve from cache if offline
- Updates: Cache automatically on successful requests
Two object stores:
notes: Stores all note data with indexes onuser_idandmodified_atsync_queue: Stores pending operations for background sync
- Automatically registers when operations fail
- Triggered when connection is restored
- Processes queued operations in order
- Updates note status after successful sync
- Synced (✓): Green checkmark - data is synchronized
- Syncing (↻): Blue spinner - currently syncing
- Pending (⏰): Yellow clock - waiting for connection
- Failed (!): Red alert - sync failed, will retry
- Mobile-first approach
- Sidebar hidden on mobile (< 768px)
- Grid/List toggle available on desktop
- Touch-friendly buttons and controls
- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Full support (iOS 11.3+)
- Samsung Internet: Full support
- Maximum 5000 characters per note content
- Maximum 100 characters per note title
- IndexedDB has browser storage limits (typically 50MB+)
- Service Worker requires HTTPS (except localhost)
- Debounced search (if implemented)
- Virtualized lists for large datasets
- Optimistic UI updates
- Efficient IndexedDB queries with indexes
- React Query caching
- Push notifications for sync completion
- Search and filter functionality
- Tags and categories
- Rich text editor
- Export/Import notes
- Collaborative editing
- Encryption for sensitive notes
MIT
Adeeko Tobiloba adeekotobiloba8@gmail.com
This project was built as part of the Pullus Frontend Assessment Test for demonstrating proficiency in:
- Progressive Web App development
- Offline-first architecture
- Service Workers and caching
- IndexedDB operations
- Background Sync API
- Modern React patterns
- TypeScript
- Responsive design