Inspiration
I've picked this project because it was technically interesting to me. As a native developer turned full-stack, I enjoy solving problems that spans multiple platforms, so the cross-device synced reminders challenge was perfect for me.
It's also a problem that affects me as I have multiple devices on multiple platforms and I keep getting reminders across all of them after nudging or dismissing one. My Apple Reminders backlog goes way back to the 2010s and I don't think I need those decade old reminders anymore.
What it does
Nudge handles reminders in real-time across all your devices:
- Create a reminder on one device, it appears on all others
- Snooze or dismiss on one device, it's handled everywhere
- No more duplicate alerts, no more stale notifications
It solves the frustration of dismissing a notification at work only to have it keep ringing at home, or creating reminders on one device but bringing a different one with you.
How we built it
Architecture Decision: While brainstorming the architecture with Opus 4.5, I evaluated three approaches:
| Approach | Dismiss Sync | Offline | Complexity |
|---|---|---|---|
| Local notifications only | Hard | Perfect | Low |
| Server push only | Easy | Good | Medium |
| Hybrid | Good | Good | Higher |
I chose server-side push because dismiss-sync becomes trivial: the server just doesn't send pushes for completed reminders. The server is the single source of truth.
Tech Stack:
- Frontend: React Native + Expo (cross-platform, streamlined build pipeline)
- State: Zustand (lightweight, works offline)
- Backend: Supabase (Auth, Postgres, Realtime, Edge Functions with Row Level Security)
- Push: Firebase Cloud Messaging (free at scale, works on both platforms)
- Scheduling: pg_cron + Edge Functions (server checks for due reminders every minute)
- Analytics: Mixpanel (tracking key user events with environment filtering)
- Error Tracking: Sentry
- Payments: RevenueCat
Challenges we ran into
The fundamental sync problem: There's no existing app that properly syncs dismiss/snooze actions across devices. The reason became clear: hard platform limitations and connectivity edge cases make a perfect solution impossible.
iOS force-quit limitation: When users swipe-kill the app, silent pushes won't wake it. This is Apple policy with no workaround. We mitigate with:
- Sync-on-launch: fetch latest states, clear stale notifications
- Notification actions ("Done" button) still work even when the app is killed
Platform-specific push handling: iOS and Android handle FCM notifications differently. On iOS, FCM delivers data via remoteMessage.data, while Android uses expo-notifications. We had to implement platform-specific listeners and carefully manage notification lifecycle across both. That's a trivial and solved problem.
EAS Build configuration:
- Firebase config files (
google-services.json,GoogleService-Info.plist) can't be committed to git for security, but EAS builds need them. Solution: inject via environment variables usingapp.config.jsdynamic configuration. - Sentry source map uploads required auth tokens we didn't have configured. We disabled auto-upload for the MVP.
React Native gesture handling: Implementing swipe-to-delete required wrapping the entire app in GestureHandlerRootView. Missing this caused cryptic "PanGestureHandler must be used as descendant" errors.
iOS simulator caching: Splash screen changes weren't appearing because the simulator caches aggressively. Had to clear with xcrun simctl erase all and delete DerivedData.
iOS 26 Liquid Glass UI: We attempted to use the new NativeTabs component for the native iOS 26 liquid glass tab bar effect, but it requires Xcode 26 beta. My laptop is still on macOS 15 and upgrading days before the deadline would be like shipping on a Friday, so I've postponed for later.
Accomplishments that we're proud of
Solved the 95% problem: Cross-device sync works reliably for the vast majority of use cases. The architecture is elegant: server sends pushes, so dismiss-sync is just "don't send the push."
Real-time sync when app is open: Supabase Realtime means if you have the app open on two devices, dismissing on one instantly updates the other.
Notification actions work even when killed: The "Done" button in notifications calls the server directly, so the OS briefly wakes the app to handle it.
Clean, warm UI: Nudge Coral (#FF6B5B) gives the app a friendly, professional feel without being childish.
Free to operate at scale: Supabase free tier, FCM unlimited, Edge Functions generous limits. The MVP and early growth cost $0.
What we learned
Why no one's solved this before: The cross-device sync problem is genuinely hard due to platform limitations (iOS background restrictions, offline scenarios). But understanding the constraints helps design around them.
Server-side architecture simplifies sync: By making the server the source of truth, we avoid complex peer-to-peer sync logic. The tradeoff (offline limitation) is acceptable with proper mitigations.
Expo + EAS is powerful but has quirks: Dynamic config with
app.config.js, environment variable injection, and native module handling all require careful setup.Platform differences run deep: iOS and Android handle notifications, background execution, and gesture systems differently. Abstraction layers help but can't hide everything.
Ship the 70% solution: A working cross-device sync that handles 95%+ of cases beats waiting for a perfect solution that may never exist.
What's next for nudge
Offline fallback: Schedule reminders locally so they fire when there's no connectivity, with sync-on-reconnect.
Polish: The app is intentionally simple: one problem, one solution. After the offline fallback, I want to polish branding and the UI, and optimise the UX.
iOS 26 liquid glass UI: Upgrading my laptop to implement the native
NativeTabsto get that glass aesthetic.Future features (post-MVP): Talking to users and gather data to decide next directions, but considering:
- Location-based reminders
- Natural language input ("remind me to call mom tomorrow at 3pm")
- Widgets (iOS/Android)
- Watch apps
- Integrations (Calendar, Slack)
Built With
- android
- edge-functions
- expo.io
- fcm
- ios
- mixpanel
- pg-cron
- postgresql
- react-native
- real-time
- revenuecat
- sentry
- supabase
- zustand
Log in or sign up for Devpost to join the conversation.