Jagoda Kuczkowska
Back to projects
2026Frontend Developer

Kairo - Habit Tracker

A cross-platform mobile app for building, tracking, and celebrating daily habits - built with React Native & Expo.

Kairo - Habit Tracker
5 screenshots

Overview

Most habit-tracker apps are either too simple to stay interesting or so complex they become another source of stress. Kairo aims to sit right in the sweet spot: a clean, motivating mobile experience that helps you build streaks, earn achievements, stay accountable with friends, and actually want to open the app every morning.

The project is actively in development - new features land regularly - and it's been as much a lesson in software engineering as it has been in building something people genuinely want to use.


Architecture & Tech Stack

The entire frontend is written in TypeScript on top of React Native and Expo, which gave us a single codebase that compiles down to native iOS and Android binaries without compromise.

  • Framework - React Native + Expo SDK - Native performance, cross-platform, huge ecosystem |
  • Routing - Expo Router (file-based) - Clean, Next.js-style navigation - zero boilerplate
  • UI Components - Tamagui - Design-system-grade primitives with built-in theming |
  • Animations - React Native Reanimated v4 - Butter-smooth 60 fps animations on the UI thread |
  • Auth state - React Context + expo-secure-store - JWT stored securely, available app-wide |
  • Notifications - expo-notifications - Scheduled local reminders per habit, per day
  • API layer - Custom apiFetch wrapper - Centralised token injection, typed error handling

Key Features

Habit Management

  • Create, edit, and delete habits with a custom emoji picker, colour picker, and category tags.
  • Assign habits to specific days of the week with configurable reminder times.
  • Track completion history and view streaks at a glance.

Smart Local Notifications

  • Each habit can have its own scheduled reminder, firing on the exact days and times the user chose.
  • On every app open, old notifications are cancelled and a fresh set is scheduled, keeping the system always in sync with the latest habit data.

Friend System

  • Add friends by their code, accept or reject incoming requests.

Achievements & Gamification

  • Unlock achievements based on streaks and completed habits.
  • Coin system rewards consistency - a small but effective motivation loop.

Theming & Accessibility

  • Full dark/light mode support via a custom ThemeContext.
  • Consistent design tokens across both platforms - what you see in the simulator is what ships.

Cross-Platform by Default

  • Every screen, animation, and component was tested on both iOS and Android throughout development. No "works on my iPhone" shortcuts.

The Challenge & Solution

Working in a Team (The Real Hard Part)

Technically, React Native wasn't the challenge. The hard part was coordinating three people across two repos (frontend + backend) without stepping on each other's work.

Early on we made a classic mistake: two of us touched the same files in parallel and ended up with merge conflicts that were painful to untangle. The fix was simple but necessary - we introduced a proper feature-branch workflow: every new screen or feature lived on its own branch, PRs required a second pair of eyes before merging, and we agreed on a shared folder/naming convention so component locations were predictable.

Notification Scheduling Complexity

Scheduling recurring local notifications sounds trivial until you realise you need to map backend day-of-week strings ("monday", "tuesday" …) to Expo's numeric weekday format, handle the case where a habit has no reminder time, and make sure stale notifications from deleted habits are always cleaned up.

The solution was a useLocalNotifications hook that owns the entire lifecycle: request permissions -> cancel all existing -> re-schedule from the current habit list. One source of truth, called once on the relevant screen. No ghost notifications.


Lessons Learned

Git is a communication tool. A clean commit history and well-scoped PRs made code review fast and blame-free. Squashing WIP commits before merging became a habit in itself.

Context API scales fine - until it doesn't. For this project, React Context is perfectly adequate for auth state and habits. But watching the HabitsContext and AuthContext grow calls me to use a more structured solution (Zustand, Redux Toolkit).

Expo is genuinely production-ready. Going in, I wasn't sure how much of the native behaviour we'd have to compromise on. Turns out - very little. expo-notifications, expo-secure-store, expo-image-picker, and expo-haptics all worked exactly as advertised on both platforms.

Parallel development requires communication and iteration. Working alongside a backend developer meant we agreed on API response shapes early on, defining TypeScript interfaces in src/types/apiTypes.ts so both sides could move forward. In practice, we still had to adjust both frontend and backend when assumptions didn’t hold up. It taught me that contracts are helpful, but flexibility and ongoing collaboration are just as important.


This project is still under active development. More features - real-time friend activity, cloud sync, and habit analytics - are already on the roadmap. Check the GitHub repo for the latest progress.

Tech Stack

React NativeExpoTypeScriptTamaguiExpo RouterReact Context API