Inspiration

Sam's brief hit close to home. The idea of switching between phones and losing all your reminders is frustrating — and it's a problem most reminder apps just ignore. They either lock you into one platform or treat syncing as an afterthought. The four requirements Sam laid out — custom snooze from notifications, powerful recurrence, real-time cross-device sync, and great design — aren't wild feature requests. They're basics that no single app gets right. That gap is what inspired SyncNap.

What it does

SyncNap is a cross-platform reminders app that syncs instantly across iOS and Android. You can:

  • Snooze directly from the notification — tap Done, 5 min, 15 min, or 1 hour without ever opening the app. On iOS, a Custom option lets you type in the exact number of minutes.
  • Set powerful recurring reminders — daily, weekly on specific days, monthly, yearly, or custom intervals. The server-side recurrence engine calculates the next occurrence automatically when you complete a task.
  • Sync across every device in real time — create a reminder on one phone, it appears on every other device instantly. Complete or snooze it, and the change propagates everywhere.
  • Swipe to act — swipe right to complete, swipe left to snooze, with haptic feedback and an undo toast on every action.

The app also has tag-based organization with emoji icons, a scrollable calendar strip, stat cards for overdue/today/snoozed counts, multiple notification sounds, and a clean freemium model powered by RevenueCat.

How we built it

The stack is React Native + Expo SDK 54 with TypeScript, using Expo Router for file-based navigation across four main tabs (Today, All, Tags, Settings) plus onboarding and detail screens.

Backend: Convex handles the database, real-time subscriptions, and server-side logic. Mutations run the recurrence engine, enforce free-tier limits, and schedule auto-unsnooze jobs using Convex's built-in scheduler.

Auth: Clerk provides Google and Apple Sign-In with secure token caching via expo-secure-store.

Notifications: Notifee handles all local notification scheduling with exact alarms, action buttons, custom sounds (5 options with dedicated Android channels), and a background event handler that syncs actions to Convex even when the app is killed.

UI: NativeWind v4 for Tailwind-based styling, react-native-reanimated for animations, expo-haptics for feedback, expo-blur for glass-effect cards, and expo-linear-gradient for the paywall and headers. Typography uses Space Grotesk for headings and DM Sans for body text.

State: Zustand manages local UI state (selected date, active filter, theme, sound preferences) while Convex subscriptions are the source of truth for all reminder and tag data.

Challenges we ran into

NativeWind + inline style conflicts on Android. Mixing className and style props on the same component caused NativeWind classes to silently drop on Android — backgrounds disappeared, text colors reverted to defaults, and positioning broke. We had to audit every component and convert mixed cases to pure inline styles. This was the single biggest source of visual bugs.

Calendar strip rendering on Android. The horizontal calendar component went through seven iterations — FlatList rendered blank on launch, ScrollView wouldn't center on today, scrollTo failed silently before layout completed. The final solution uses fixed card sizes, contentOffset as a prop, and a visibility flag that hides the strip until it's correctly scrolled to today.

Background notification actions. Getting Done and Snooze to work from the notification when the app is killed required a separate code path in index.js using ConvexHttpClient for direct HTTP mutations, since the normal authenticated client isn't available in the background handler.

Subscription status flicker. RevenueCat and Convex's isPro flag would briefly disagree on app launch, causing Pro badges and limits to flash. We debounced the state and prioritized the Convex value until RevenueCat confirmed.

Accomplishments that we're proud of

  • Notification actions that actually work in the background — snooze or complete a reminder from the notification shade, and it syncs to the server and reschedules the next notification, all without opening the app.
  • Server-side auto-unsnooze — when you snooze a reminder, Convex schedules a job that automatically marks it active again when the snooze expires. No client-side timers needed.
  • The recurrence engine — handles hourly, daily, weekly (with specific days), monthly, yearly, and custom intervals, including auto-advancing past missed occurrences and respecting end dates.
  • 50+ tag-to-emoji mappings — reminder cards show contextual emoji badges (💼 for Work, 🏠 for Home, 💪 for Fitness) that make scanning a list of reminders instant.
  • Real-time sync that just works — Convex subscriptions mean every screen is always up to date across every device with zero manual refresh logic.

What we learned

  • NativeWind v4 on Android requires careful handling — never mix className and style on the same component, or the Tailwind classes get silently ignored.
  • Notifee's background event handler runs in a different JavaScript context, so you can't use hooks, providers, or authenticated clients. Direct HTTP calls are the way.
  • Convex's scheduler is incredibly useful for time-based server logic (auto-unsnooze, future: recurring notification rescheduling) without needing cron jobs or external services.
  • Building a polished mobile app is 30% features and 70% edge cases — Android scroll behavior, dark mode consistency, haptic timing, toast positioning, and font loading all take more time than the core logic.

What's next for SyncNap

  • Push notifications for cross-device sync — currently, if a reminder is created on Device A and the app is closed on Device B, Device B won't schedule the notification until the app is reopened. Silent push notifications will solve this.
  • Widgets — iOS and Android home screen widgets showing today's reminders and quick-add shortcuts.
  • Location-based reminders — trigger reminders when arriving at or leaving a place.
  • Collaboration — share reminder lists with family or teammates.
  • Offline support — queue mutations when offline and sync when connectivity returns.

Built With

  • clerk
  • convex
  • date-fns
  • eas
  • expo-blur
  • expo-haptics
  • expo-linear-gradient
  • expo-router
  • expo-sdk-54
  • expo.io
  • javascript
  • lucide-react-native
  • nativewind
  • notifee
  • react-native
  • react-native-gesture-handler
  • react-native-reanimated
  • revenuecat
  • typescript
  • zustand
Share this project:

Updates