How to Build a React Native App with AI: A Habit Tracker Walkthrough
A real walkthrough of building a habit tracker app with an AI agent - exact prompts, generated file structure, Expo commands, and the mistakes to avoid.
What You're Actually Building
Let's skip the abstract and build something. By the end of this post, you'll have a working React Native habit tracker app generated by an AI agent, running on your phone or in a simulator. I'm going to show you the exact prompts I used, every file the agent generated, what to do with the output, and the mistakes I made the first few times that you can avoid.
If you want to understand how the AI agent works under the hood before jumping into this tutorial, read What Is an AI Agent App Builder? first. This post focuses on the practical how-to.
Why a Habit Tracker?
I picked a habit tracker because it hits the right complexity for a tutorial - complex enough to be realistic, simple enough to be legible. A good habit tracker needs:
- Multiple screens (home, add habit, habit detail, statistics)
- Local state that persists across sessions
- Data visualization (streak counts, completion rates)
- A clean component architecture (habit cards, progress rings, etc.)
- Tab navigation and stack navigation together
If an AI agent can handle this well, it can handle most apps you'd want to build at the MVP stage.
The Prompt
Here's the exact prompt I submitted. I'll explain why I wrote it this way afterward:
Build a React Native habit tracker app using Expo and TypeScript. The app should have four tabs: Today (shows today's habits with checkboxes), Habits (manage all habits, add/delete), Stats (shows completion rates and streaks per habit as simple bar charts), and Profile (user name, total habits completed this month, settings toggle for notifications). Use a clean, minimal design with a white background and indigo (#6366f1) as the primary accent color. Include 5 realistic sample habits in the mock data. Habits should have a name, frequency (daily/weekly), and category (health, productivity, mindfulness, fitness). Use AsyncStorage for persistence.
Let me explain the decisions in that prompt:
Specify the framework explicitly. "React Native" and "Expo" and "TypeScript" - not just "mobile app." Vague prompts produce vague architecture decisions. Name all four screens. Don't make the agent decide what screens you need. You know your app; tell it. This prevents it from generating a three-tab app when you wanted four, or inventing a screen you didn't want. Specify the color scheme with hex values. "Nice colors" produces different results every time. "#6366f1 as the primary accent" produces the same result you'd pick in Figma. Include 5 realistic sample habits. Mock data matters for demos. If you leave it to the agent, you get habits like "Habit 1", "Habit 2". Specifying "5 realistic sample habits" and the data shape (name, frequency, category) means the demo actually looks like a real product. Name the persistence layer. AsyncStorage is a known, well-documented React Native library. Saying "store data locally" leaves the agent to choose, and it might pick something more complex than you need.What the Agent Generated
The agent produced 19 files in about 90 seconds. Here's the complete structure with brief notes on each file:
``
habit-tracker/
├── package.json # Expo SDK 51, RN 0.73, deps declared
├── app.json # Expo config with correct bundle IDs
├── tsconfig.json # Strict mode, path aliases configured
├── babel.config.js # Expo preset
├── app/
│ ├── _layout.tsx # Root layout with tab navigator
│ ├── (tabs)/
│ │ ├── _layout.tsx # Tab bar with icons and labels
│ │ ├── index.tsx # Today tab - main screen
│ │ ├── habits.tsx # Habit management tab
│ │ ├── stats.tsx # Statistics tab
│ │ └── profile.tsx # Profile tab
├── components/
│ ├── HabitCard.tsx # Swipeable habit card with checkbox
│ ├── ProgressRing.tsx # SVG progress circle for streaks
│ ├── CategoryBadge.tsx # Pill badge for habit categories
│ ├── EmptyState.tsx # Shared empty state component
│ └── StatBar.tsx # Horizontal bar chart for stats
├── hooks/
│ ├── useHabits.ts # Main data hook with AsyncStorage
│ └── useStats.ts # Derived statistics calculations
├── types/
│ └── habit.ts # TypeScript interfaces for Habit, Log, Stats
├── constants/
│ ├── theme.ts # Colors, spacing, font sizes, shadows
│ └── sampleData.ts # 5 pre-seeded realistic habits
└── utils/
└── dateUtils.ts # Date formatting and streak calculation
`
19 files. TypeScript throughout. Proper separation of concerns - hooks, types, constants, and utilities are all in their own directories. This is the architecture a mid-level React Native developer would produce. The agent didn't just write code; it made architecture decisions.
A Look at the Generated Code
The types/habit.ts file sets the foundation:
`typescript
export type HabitCategory = "health" | "productivity" | "mindfulness" | "fitness";
export type HabitFrequency = "daily" | "weekly";
export interface Habit {
id: string;
name: string;
category: HabitCategory;
frequency: HabitFrequency;
createdAt: string;
color: string;
}
export interface HabitLog {
habitId: string;
completedAt: string; // ISO date string
date: string; // YYYY-MM-DD for lookup
}
export interface HabitStats {
habitId: string;
currentStreak: number;
longestStreak: number;
completionRate: number; // 0-1
totalCompletions: number;
}
`
Clean, explicit types. Every field is typed. The separation between HabitLog (the event record) and HabitStats (the derived view) is a real architectural decision that a lot of junior developers miss.
The hooks/useHabits.ts hook manages persistence:
`typescript
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useState, useEffect, useCallback } from "react";
import { Habit, HabitLog } from "@/types/habit";
import { sampleHabits } from "@/constants/sampleData";
const HABITS_KEY = "@habits";
const LOGS_KEY = "@habit_logs";
export function useHabits() {
const [habits, setHabits] = useState
const [logs, setLogs] = useState
const [loading, setLoading] = useState(true);
useEffect(() => {
loadData();
}, []);
async function loadData() {
try {
const [habitsJson, logsJson] = await Promise.all([
AsyncStorage.getItem(HABITS_KEY),
AsyncStorage.getItem(LOGS_KEY),
]);
setHabits(habitsJson ? JSON.parse(habitsJson) : sampleHabits);
setLogs(logsJson ? JSON.parse(logsJson) : []);
} finally {
setLoading(false);
}
}
const toggleHabit = useCallback(async (habitId: string, date: string) => {
const existingLog = logs.find(
(l) => l.habitId === habitId && l.date === date
);
const newLogs = existingLog
? logs.filter((l) => !(l.habitId === habitId && l.date === date))
: [...logs, { habitId, completedAt: new Date().toISOString(), date }];
setLogs(newLogs);
await AsyncStorage.setItem(LOGS_KEY, JSON.stringify(newLogs));
}, [logs]);
// addHabit, deleteHabit implementations follow...
return { habits, logs, loading, toggleHabit };
}
`
This is not template code. The Promise.all for parallel storage reads, the useCallback on toggleHabit to prevent unnecessary re-renders, the fallback to sampleHabits on first load - these are patterns an experienced React Native developer would write.
Running the Output
Download the zip, extract it, then:
`bash
# Navigate into the project
cd habit-tracker
# Install dependencies
npm install
# Start the Expo dev server
npx expo start
`
You'll see a QR code in the terminal. Scan it with the Expo Go app on your phone (iOS or Android) and the app loads over your local network. No Xcode, no Android Studio, no simulators required for the initial preview.
If you want to run in a simulator:
`bash
# iOS simulator (Mac only, requires Xcode)
npx expo start --ios
# Android emulator (requires Android Studio)
npx expo start --android
`
The app runs. First-party navigation, real AsyncStorage persistence, working stats calculations. Not a mockup.
Iterating with Chat
After the first generation, I sent three follow-up messages to refine:
Message 1: "The habit cards on the Today screen are too tall. Make them more compact - reduce the padding and make the habit name smaller. Also add a subtle bottom border instead of the card shadow." Message 2: "Add a long-press gesture on habit cards that shows a delete confirmation alert. Use React Native's Alert API." Message 3: "The stats screen only shows completion rate. Add a 7-day mini-calendar view above each stat bar showing which days the habit was completed this week, using colored squares."Each iteration took 30-60 seconds and the agent updated only the affected files, preserving everything else.
Common Mistakes to Avoid
I've watched enough people use AI app builders to know where things go wrong. Here are the patterns I see repeatedly:
Mistake 1: Prompts that are too vagueBad: *"Build me a habit tracker app"*
This gets you a generic result. The agent makes all the decisions - navigation structure, color scheme, data model, screen count. Some of those decisions will be wrong for your specific needs.
Good: Specify screens, colors, data shape, and the one feature that makes your app different from every other habit tracker. The more context you provide, the more the output matches what you actually wanted.
Mistake 2: Trying to build too much in one promptBad: *"Build me a habit tracker with social features where users can follow each other, see friends' habits, send encouragement, create habit challenges together, and compete on leaderboards"*
When you cram 6 major features into one prompt, you get a shallow version of all of them. The agent doesn't know which features matter most to you, so it spreads effort evenly across all of them.
Good: Start with the core - the single most important thing the app does. Get that working first. Then add features one at a time through chat iterations.
Mistake 3: Treating the first output as finalThe first generation is the starting point, not the deliverable. Every professional who uses these tools iterates 3-8 times before they're satisfied. Plan for it. Reserve time for iteration.
Mistake 4: Not checking the generated package.jsonThe agent picks library versions based on its training data. By the time you read this, some of those versions may be outdated. After downloading, open package.json and verify the Expo SDK version matches the current stable release (currently SDK 51). If it doesn't, update the relevant dependencies before running npm install.
Run npx tsc --noEmit after downloading. Most of the time it passes clean. Occasionally the agent will produce a minor type inconsistency - usually an interface missing an optional field or a function expecting the wrong type for a callback. These are quick fixes, but you want to find them before you're three hours into building on top of the foundation.
What You Own
This is worth saying directly: the code you download belongs to you. There's no licensing restriction, no "built with iBuildYourApp" watermark requirement, no SDK you're locked into. It's plain React Native code that runs with Expo. Any React Native developer can read it, modify it, and extend it.
The habit tracker I generated in this walkthrough became the foundation for a real side project. The core habit management logic from useHabits.ts` is still running, unchanged, in production.
That's what differentiates agent-generated code from no-code platforms. For more on that distinction, see AI Agent App Builder vs No-Code Platforms.
Ready to build your app?
Start Building for FreeKeep reading
How to Build a Marketplace App with AI
A complete tutorial on building a two-sided marketplace app with AI - buyer/seller flows, product listings, search and filters, cart, and profiles. Real prompts and generated architecture.
ReadtutorialHow to Build a Restaurant Website with AI
A step-by-step tutorial for building a professional restaurant website with AI - from menu pages to reservation forms. Exact prompts, generated code, and iteration tips.
ReadtutorialHow to Build a Booking App with AI Using React Native
A step-by-step tutorial for building a complete booking and appointment app with an AI agent - exact prompts, generated architecture, iteration strategy, and the specific patterns that make booking flows work in React Native.
Read