How to build a fitness app in React Native with Expo (screens, stack, and code)
The screens a fitness tracking app needs, the Expo and React Native stack to build them with, and the routing, theming and chart code to get from empty project to working app.
September 14, 2026 · Thomino
Fitness apps are one of the best first mobile projects. The domain is simple to understand, the data model is small, and every screen type you'll ever need shows up somewhere: an onboarding quiz, a dashboard with charts, forms, lists, a detail view, a live tracker, and a paywall. Build one and you've learned most of React Native.
This post walks through what to build and how, using the stack we use for Caloria, our fitness and meal tracking template. Everything here applies whether you start from scratch or from the template.
What a fitness app actually needs
Before writing code, list the screens. Here's the set that covers a calorie and workout tracker end to end, taken from Caloria's route tree:
Onboarding and auth
- Welcome screen
- Onboarding quiz: sex, birthday, height, current weight, weekly weight trend, exercise frequency
- Login, signup, forgot password
- Notification and location permission prompts
Main tabs
- Dashboard: greeting, weight trend for the last 7 days, calories this week, sleep, water intake
- Meals: today's log with add-meal entry
- Workouts: recent and available workouts, each opening a detail screen and a live tracker
- Progress: weight history with entries, current weight, and weight lost
Everything else
- Add meal, add water, add weight, add workout
- Workout detail and workout tracker
- Profile and edit profile
- Notifications list
- Connected devices
- Subscription paywall
- Help
That's 25 screens. The dashboard and progress tab are the two that make or break the app, because they're where users see whether their effort is working.
The stack
This is what Caloria runs on, and what I'd pick for a new fitness app today:
| Need | Package |
|---|---|
| Framework | Expo SDK 57, React Native 0.86, TypeScript |
| Navigation | Expo Router with a drawer wrapping a tab navigator |
| Styling | NativeWind v4 (Tailwind classes in React Native) |
| Charts | victory-native for line and area charts, react-native-chart-kit for quick bar charts |
| Animation | react-native-reanimated and react-native-gesture-handler |
| Long lists | @shopify/flash-list |
| Dates | @react-native-community/datetimepicker |
| Reminders | expo-notifications |
| Photos | expo-image-picker for progress photos and meal photos |
| Icons | lucide-react-native |
No backend is in that list on purpose. Get the UI right against local mock data first. Wiring Supabase or Firebase later is a day's work; redesigning screens around a data model you got wrong is a week's.
Project structure
Expo Router maps your file tree to routes. This layout gives you a drawer for secondary navigation and tabs for the four main screens:
Screens that aren't tabs live under screens/ and are pushed on top of the tab stack, so the tab bar hides while you're adding a meal and comes back when you're done.
Theming from day one
Fitness apps get used at 6am and 11pm. Dark mode isn't optional. The cheapest way to support it is to never write a color in a component. Define your palette as CSS variables in a Tailwind config, expose them as classes like bg-background, text-subtext and bg-highlight, and switch the variable set when the theme changes.
Then every component is written once:
For chart libraries and icons that need a JS color value rather than a class, keep a useThemeColors() hook that returns the same palette as an object.
The onboarding quiz
The quiz is a multi-step form. Each step is one question, the answer is stored in local state, and the last step computes a daily calorie target. Build it as a generic MultiStep component that takes an array of step components and handles progress and navigation, then the quiz itself is just data:
Keep each step to a single tap where you can. Selectable chips beat text inputs for sex, trend and activity. A wheel picker beats typing for height and weight.
The dashboard
The dashboard is a scroll of cards. Each card owns its own data and chart, so you can reorder or remove them without touching anything else. A weight trend card with victory-native looks like this:
Repeat the pattern for calories this week, water intake and sleep. Small cards in a two-column grid, a full-width card for the primary metric.
Logging entries
Add-weight, add-water and add-meal are the same screen with different fields: a header with a back button, one or two inputs, a date picker defaulting to now, and a full-width save button. Write one EntryForm layout and pass the fields in. The important detail is that saving should navigate back and the tab underneath should already show the new entry. With local state that's a context update; with a backend it's an optimistic write.
The workout tracker
The tracker is the one screen with real interaction: a timer, the current exercise, sets and reps, and next and previous controls. Keep the timer in a useEffect with setInterval, store elapsed seconds in state, and derive the display string. Use react-native-reanimated for the progress ring so it doesn't stutter while the timer ticks.
Reminders
Water and workout reminders are the single feature that most improves retention, and expo-notifications makes local scheduling a few lines:
Ask for permission on a dedicated screen after onboarding, with a sentence explaining why. Permission prompts fired on first launch get denied.
The paywall
Design the subscription screen even if you don't sell anything yet: three plan cards, one highlighted, a feature list, and a restore purchases link. When you're ready, RevenueCat or the native StoreKit and Play Billing wrappers plug into that screen without changing its layout.
Ship it
Once the screens work against mock data:
- Run
npx expo-doctorand fix what it reports. - Set your bundle identifier and package name in
app.json. - Replace mock data and placeholder images.
- Build with
eas buildor locally withnpx expo run:iosandnpx expo run:android.
Or start from Caloria
Everything above exists, working, in Caloria: the 25 screens, the drawer and tab navigation, the theme system with light and dark mode, 52 reusable components, and the charts. It's on Expo SDK 57.0.22, TypeScript throughout, and it's a front-end template with local mock data, so you connect whatever backend you like. It costs $99, or you can get every template together.
If you'd rather build it yourself, the structure and patterns above are the ones the template uses, and they'll hold up as the app grows.