Inspiration
Moving out for the first time is exciting until you realize nobody is telling you to drink water, go to bed, or put your phone down. Most health apps feel like forms and dashboards, and they're easy to ignore. A mom's nagging is hard to ignore because it comes with warmth and a little guilt. We wanted to put that in your pocket: Mothrly, an AI companion for anyone living away from home for the first time.
What it does
Mothrly is a warm, slightly nagging presence that:
- Reminds you to hydrate, sleep on time, and stay focused, and says it out loud with persona-specific text-to-speech.
- Changes personality: pick Gentle, Strict, Funny, or Motivational, and all the copy is rewritten in that voice.
- Supervises your scrolling: when you cross a time threshold in a watched app, Mothrly draws a nudge over that app, so the nag lands while you're still in TikTok, not when you next open Mothrly.
- Keeps it fun with daily streaks and GIF mood check-ins instead of boring forms.
- Monetizes through RevenueCat, with premium personas and voices unlocked via entitlement.
How we built it
- App: Expo SDK 57, React Native 0.86, React 19, and strict TypeScript, with Expo Router for file-based navigation and Zustand for state.
- Navigation: onboarding and the main app are separated with
Stack.Protectedguards, so flipping one flag moves the user between them with no imperative navigation. - Offline-first storage: there is no server. A three-tier synchronous key-value store (MMKV → expo-sqlite → in-memory) sits behind Zustand's persist, and each tier is loaded defensively so a missing native module can't crash startup.
- Supervise mode: a custom local Expo module with a Kotlin foreground service polls Android's
UsageStatsManagerand draws the overlay nudge. It keeps running and restarts itself without JavaScript, and any nudges raised while JS was dead are folded back in on next launch. - Personas as pure data: nothing in the code branches on which persona is active, so adding a fifth persona is a single data entry.
- Subscriptions: RevenueCat (
react-native-purchases+-ui) handles thepremium_personasentitlement and paywall. - Polish: hand-drawn SVG icons, Reanimated animations, and a hidden 7-step guided demo mode we built for screen recordings.
Challenges we ran into
- Counting screen time correctly. Aggregated usage buckets can start before midnight and would count last night as today. We switched to raw resume/pause events and compute today's usage as:
$$T_{\text{today}} = \sum_{i} \left( \min(t^{\text{pause}}i,\, t{\text{now}}) - \max(t^{\text{resume}}i,\, t{\text{midnight}}) \right)$$
Event types are matched numerically because constant names differ across Android versions.
- Awkward permissions. Usage access and "Display over other apps" have no runtime prompt and live deep in system settings, so we built explanation screens that walk people there.
- A service that must outlive JavaScript. Config is passed to the Kotlin service as a JSON string it persists verbatim, so it can restart itself with no JS running.
- Native code vs. Expo Go. The local native module means Expo Go can't run the app, so a dev build is required. Everything reports "unsupported" rather than throwing, so iOS falls back to seeded data.
- Fragile startup. A missing native module throws during module evaluation, which is why every storage tier is loaded inside a
try.
Accomplishments that we're proud of
- The overlay nudge works: Mothrly nags you inside the app you're overusing.
- The persona system is clean and extensible, and every persona comes with its own voice.
- Reminders run end to end, with rolling-horizon scheduling that tops up on each launch.
- The full RevenueCat flow works, including the paywall UI.
- The codebase is well documented and has zero TODO/FIXME comments.
What we learned
- Writing a native Android service and bridging it into Expo through a local module.
- How Android usage stats really work, and why raw events are more trustworthy than aggregates.
- Designing offline-first state that degrades gracefully when native modules are missing.
- Wiring up RevenueCat entitlements so purchases drive real feature unlocks.
- Treating tone as a product feature: the same reminder lands very differently in a Gentle voice versus a Strict one.
What's next for Mothrly
- Real data on Home: the hydration, sleep, and focus tiles currently use placeholder values.
- Better iOS support, where supervise mode currently runs on seeded data.
- Production RevenueCat: platform-specific store keys instead of the shared Test Store key, then a Play Store release (with a justification for the foreground service type).
- Fully customizable voices: users will be able to upload or record their own voice samples, so Mothrly can sound just like their own mother's voice, with the reminders and nudges delivered in a voice they actually miss.
- Adaptive personas: instead of picking one personality, Mothrly will switch between Gentle, Strict, Funny, and Motivational based on the user's mood and on how many times she has had to interrupt them to stop scrolling. A stressed user gets gentleness, and a repeat offender gets the strict voice.
- Personal reminders: users will be able to hand Mothrly their own to-dos, like "call the landlord" or "submit the assignment by Friday", and she'll remind them in her own persona's voice so nothing slips through the cracks.
Built With
- react-native
- revenuecat
Log in or sign up for Devpost to join the conversation.