-
-
Elio, a baby tracker made for short nights, on iPhone and Android
-
The day dial: every feed, nap and diaper at its hour on a 24-hour ring
-
Dark by default, calm enough to check at 3 a.m.
-
A feed in two taps, and sleep picked on time wheels with no stopwatch
-
This week against last, in plain words
-
The Premium assistant answers from the baby's own logs and names its health source
-
Mood, mental load and sleep for the parent, with a private diary
-
Everything you log is free, and Premium adds the parts that think for you
-
One shared history for the whole family, on iPhone and Android
-
Formula recall alerts from France's official register, free on every plan
-
Lock Screen and Home Screen widgets show the last feed at a glance
-
Five languages, and each baby follows its own country's health guidance
-
The phone saves first, and the server alone decides who is Premium through RevenueCat
Elio is a baby tracker built for 3 a.m. It logs feeds, sleep, diapers and growth in a couple of taps, shows the whole day on one dial, looks after the parent as well as the baby, and answers questions from the baby's own logs. It works offline, on iPhone and Android, in five languages, for babies followed in six countries.
Categories I'm entering: RevenueCat Design Award · RevenueCat Peace Prize
Inspiration
New parents log a lot of data at the worst possible moment: at night, one-handed, half asleep, in a dark room. Most baby trackers are designed for daytime. They have bright screens, dense grids and, above all, a stopwatch: tap start when she falls asleep, tap stop when she wakes. At 3 a.m. nobody remembers to tap stop, so the data breaks and the app ends up making a tired parent feel worse.
I wanted a tracker that asks less of a tired person. It had to be readable in the dark, and it should never nag or guilt-trip. It should look after the parent too, because postpartum exhaustion and anxiety happen during those same nights. And anything that keeps a baby safe should cost nothing.
What it does
- The day dial. Every feed, nap and diaper sits at its hour on a 24-hour ring. Midnight is at the top and time runs clockwise, so an event at minute $t$ of the day lands at $\theta = 360^\circ \cdot \frac{t}{1440}$. One glance tells you how long she has been awake and when she last ate.
- Logging without a timer. A feed starts from the last one, so saving takes two taps. A sleep is a start and an end on two time wheels, with "just fell asleep" and "just woke up" shortcuts. Elio works out the duration, and nothing can be left running.
- Insights in plain words. This week against last, night wake-ups, growth against the WHO curves, and a short note each morning drawn from your own entries.
- A space for the parent. Mood, mental load and your own sleep on a few sliders, plus a private diary that nobody else in the family can read.
- An assistant that knows your nights (Premium). It answers from the baby's own history and from your country's health sources, and names the source. It never diagnoses, and warning signs always point to the local emergency number.
- The whole family (Premium to invite, free to join). A co-parent, a grandparent or a nanny joins with a code or a QR, each with the access you choose. A care day prepares notes for whoever is looking after the baby that day.
- Safety stays free. For babies followed in France, Elio reads the official recall register every day and warns you when your formula is on it. Recall alerts, red flags and the solids safety rules sit on the free side of the feature matrix for good.
- Six country packs. France, Germany, Spain, Italy, the US and the UK, each with its own vaccines, checkups, emergency numbers, solids window and health sources.
- Around the edges. Lock Screen and Home Screen widgets on iPhone, a PDF summary for the pediatrician, solids with allergens and reactions, and milestones.
How I built it
Elio is an Expo SDK 56 app (React Native 0.85, React 19, the New Architecture) in strict TypeScript, with Expo Router and native tabs. I built it alone: about 60,000 lines of TypeScript over 125 commits since June 2026.
The phone comes first. Every tap writes to a Zustand store that is persisted to an encrypted MMKV instance, with its key in the keychain. The app is fully usable with no network. Supabase is the sync layer: Postgres in the EU with row-level security per child, a full pull when the app opens, and a silent push that wakes the other parent's phone when something changes. There are no websockets, to save battery.
A night-first design. Dark is the default: lavender on near-black, warm accents for feeds and diapers, hairline borders, Fraunces for display type and thin Tabler icons. Body text passes WCAG AA contrast on the night background, charts can be read by VoiceOver, and text scales with the system size. Reanimated and haptics give each save a small physical response.
RevenueCat, with the server as the judge. react-native-purchases powers the paywall's offerings, purchase and restore, and the free trial is read from the product's intro price. The paywall's feature list is generated from a single feature matrix (config/features.ts), so the sales copy can never promise something the matrix gives away for free. On top of that sit three rules:
- The RevenueCat customer is always the Supabase user.
Purchases.logIn(userId)runs before every tier read, purchase and restore. The server only uses the id from the JWT, so a patched client can't borrow someone else's subscription. - Only the server decides who is premium. An
entitlementEdge Function asks RevenueCat's API and writes the answer to a column no client can write. The assistant, family invites and the photo storage policies each check that answer themselves. - The webhook doesn't interpret events. RevenueCat sends a dozen event types, and mapping them is where subtle bugs live: a cancellation turns off renewal but doesn't end access today. So
revenuecat-webhookcollects every account an event mentions and asks RevenueCat what is true for each one. That handles every event type, transfers included, and stays correct when an event is retried.
A hidden tester tier, granted by hand in Supabase, lets people try Premium without buying it.
The assistant. The Gemini key never ships in the app. A Supabase Edge Function checks the tier and a daily quota (30 questions a day on Premium, 3 in total on the free tier), then calls Gemini with function calling. The model asks for what it needs through ten tools such as get_sleep and search_journal, and the phone answers from its own history. Answers are grounded in 1,373 passages written from the pages of national health bodies (Santé publique France, the NHS and the AAP among them), 37 topics per country, each passage carrying its source. They are retrieved with pgvector and filtered by the baby's country and the parent's language.
Shipping. EAS builds run from a GitHub Actions button, with a guard that refuses any build number a store has already seen. The store listings for six languages, screenshots included, are pushed from the repo by script. The screenshots are HTML drawings built from the app's own design tokens and rendered by headless Chrome, and so is this gallery. A remote brake can stop an old build that is already on phones.
The numbers. A typical Premium user costs about 12 US cents a month in Gemini calls. With fixed costs of about €49 a month (Apple, EAS, the domain and Supabase Pro) and about €3.80 of net revenue per paying account after VAT, store fees and refunds, Elio breaks even at
$$ n = \left\lceil \frac{49}{3.80} \right\rceil = 13 \text{ paying families.} $$
Challenges I ran into
- Nothing synced, and nothing said so. Children are written with an upsert, and Postgres checks an upsert against the UPDATE policy as well as the INSERT one. At that moment the parent who created the child wasn't an admin yet, because their membership row comes from a trigger that runs after the insert. So every first push was refused, and then every log belonging to that child. The client was also pushing logs before their child. Both halves needed fixing.
- A parent who paid and got nothing. At first the RevenueCat login only ran inside the assistant, which a free parent can't open. Someone who subscribed stayed anonymous to RevenueCat, so the server refused their invites and photo uploads, and the next daily check put them back on the free tier. Now the binding happens before every tier read, and the webhook writes the answer without waiting to be asked.
- Models retired mid-build. In September Google stopped serving the models the assistant used. Every answer failed while embeddings kept working. Each task now walks a chain of models until one accepts.
- English isn't a country. The first English version was really France in English: French vaccines, French recalls, the French emergency number, solids from 4 months. Splitting language (per phone) from country (per baby) turned into six packs of rules, knowledge and store listings.
- Designing for 3 a.m. meant removing things. The stopwatch went, then the bright cards, then extra words on the free tier, then anything that reads as a warning when a baby is doing something perfectly normal. A food a baby refuses says "not this time", never "refused".
What I learned
- Design for the worst moment of use. If it works one-handed in the dark, it works at noon.
- Money needs one source of truth, and it has to be the server. The app only shows what the server has already decided.
- Asking beats interpreting. Re-reading RevenueCat on every webhook event removed a whole class of bugs.
- Where the paywall goes is a values decision. The safety features are written into the feature matrix as free, with a comment saying they never move.
- Read the receipt before believing a push arrived. The send API answers "ok" long before it knows.
What's next
Getting Elio into parents' hands in the six launch countries, reading every piece of feedback, and adding a plan for the wider family (grandparents and nannies) if parents ask for it.
Built With
- expo.io
- gemini
- react-native
- revenuecat
- supabase
Log in or sign up for Devpost to join the conversation.