Build a Mobile App with Claude Code
Build a habit tracker app on iOS Simulator using Claude Code in VS Code.
Introduction
30 Second Summary
Building a mobile app used to mean weeks of setup, debugging, and searching for answers. AI coding tools have changed that completely. Now you can go from idea to working app in a single session.
In this project, you will build a complete habit tracker mobile app using Claude Code in VS Code. You'll progress from beginner prompts to fully autonomous AI-driven development, mastering every major Claude Code feature along the way.
What You'll Build
A "Daily Habits" tracker app running on the iOS Simulator, built progressively using Claude Code's full feature set from basic prompting to autonomous building.
By the end of this project, you'll have:
- A working multi-screen mobile app with habit tracking, persistent local storage, weekly stats, and dark mode.
- Mastery of Claude Code's complete workflow: prompting, CLAUDE.md project memory, Plan mode, permission modes, checkpoints, @-mentions, Extended Thinking, and Auto mode.
- A reusable CLAUDE.md template and prompting patterns you can apply to any future mobile project.
- Secret Mission: Use Auto mode to have Claude autonomously build a complete habit details screen with edit and delete functionality.
Are there any prerequisites?
You need a Claude Pro or Max subscription (required for Claude Code), Xcode with iOS Simulator installed on your Mac, and Node.js v20 or higher.
Install Claude Code and Create Your Project
Before you can build a mobile app with AI assistance, you need two things: Claude Code running inside VS Code, and an Expo project to work on.
In this step, you will install the Claude Code extension, scaffold a new Expo project, and confirm it runs on the iOS Simulator.
In this step, get ready to:
- Install and sign in to the Claude Code VS Code Extension.
- Scaffold a new Expo SDK 56 project called daily-habits.
- Verify the app runs on the iOS Simulator.
Install the Claude Code VS Code extension
Claude Code is an AI coding assistant that lives inside VS Code. It reads your actual project files and makes edits directly in your codebase.
- Press Cmd+Shift+X to open the Extensions view.
- Type Claude Code in the search bar.
- Click Install on the Claude Code extension by Anthropic.
Once installed, you need to open Claude Code in its own panel.
- Open the Command Palette with Cmd+Shift+P.
- Type Claude Code and select Open in New Tab.
- Sign in with your Anthropic account when prompted.
- Complete the onboarding checklist that appears (click Show me on each item to walk through the basics).
What is Claude Code?
Claude Code is an AI assistant that reads and edits your actual project files. Unlike a generic chatbot, it understands your codebase and can propose precise changes as diffs you review before accepting.
✔️ I see the Claude Code panel
Great. You should see a prompt box where you can type messages to Claude. Move on to the next substep.
ⓧ I'm seeing a sign-in error
Make sure you have an active Claude Pro or Max subscription. Free-tier accounts do not include Claude Code access.
- Open https://console.anthropic.com in your browser and verify your subscription is active.
- Return to VS Code and try signing in again from the Claude Code panel.
If you are still stuck, help me troubleshoot Claude Code sign-in.
Create a new Expo project
Expo is a framework that simplifies React Native development. The default SDK 56 template comes preconfigured with TypeScript, file-based routing, and everything you need to build a multi-screen app.
- Open VS Code's integrated terminal by pressing Ctrl+` (macOS) or Ctrl+` (Windows).
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your Expo project by running this command:
npx create-expo-app@latest daily-habits --template default@sdk-56
What does this command do?
npx create-expo-app@latest runs the latest version of Expo's project generator.
daily-habits is the name of your project folder. --template default@sdk-56 tells it to use the SDK 56 template with Expo Router and TypeScript preconfigured.
The scaffolding takes a minute or two to download dependencies.
- Once it finishes, open the new project in VS Code by running these commands:
cd daily-habits && code .
Use the new window
A new VS Code window opens with your daily-habits project. Do the rest of this project in that new window.
Seeing a 'command not found' error for code?
The code command needs to be installed in your PATH. In VS Code, open the Command Palette (Cmd+Shift+P), type Shell Command, and select Install 'code' command in PATH. Then try the command again.
help me fix the code command not found error on macOS.
Verify your setup on the iOS Simulator
Before building anything custom, confirm that the default template runs correctly on the iOS Simulator.
- Open the integrated terminal in your new VS Code window (Ctrl+`).
- Start the Expo development server by running:
npx expo start
- Once you see the QR code and menu in your terminal, press I to open the app in the iOS Simulator.
What happens when you press I?
Expo CLI launches the iOS Simulator (via Xcode) and automatically installs Expo Go on it. Expo Go is a container app that loads your project code without needing a full native build.
After a few moments, you should see the default Expo template app running in the simulator.
- Press Ctrl+C in the terminal to stop the development server.
✔️ I see the default app in the simulator
Your environment is fully working. The iOS Simulator, Expo, and your project are all connected.
ⓧ The simulator didn't open
This usually means Xcode's command-line tools aren't linked properly.
- Open the Terminal app (outside VS Code).
- Run this command to set the Xcode developer directory:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
- Return to VS Code and try npx expo start again, then press I.
help me get the iOS Simulator working with Expo.
ⓧ I see a red error screen in the simulator
A red screen means the app loaded but hit a JavaScript error. This is rare with a fresh template.
- Press Ctrl+C to stop the server.
- Delete the node_modules folder and reinstall by running:
rm -rf node_modules && npm install
- Run npx expo start and press I again.
help me fix a red error screen in Expo on iOS Simulator.
Your tools are ready and your project is running. Next up, you will send your first prompts to Claude Code and watch it edit your files in real time.
Send Your First Claude Code Prompts
Your project is scaffolded and the simulator is working. Now it's time to actually use Claude Code to start shaping your app.
Claude Code reads your actual codebase and responds with real edits, not generic suggestions. In this step, you'll learn the core loop: prompt, review the diff, accept or reject, and iterate.
In this step, get ready to:
- Explore your project structure using a Claude Code prompt.
- Make your first code edit through Claude Code and see it on the simulator.
- Practice the reject-and-refine cycle to iterate on Claude's output.
Send your first prompt
Claude Code can read every file in your project and answer questions about it. Your first prompt will ask Claude to explain what's in the project.
- Open the Claude Code panel by clicking the Spark ✱ icon in the Status Bar at the bottom-right corner of VS Code.
- In the prompt box, type the following and press Enter:
What files are in this project and what does each one do?
Claude reads your project files and responds with a summary of the structure.
What just happened?
Claude Code scanned the files in your daily-habits folder and explained each one. This is how Claude Code works: it reads your actual codebase, not a generic template.
You'll see files like app/index.tsx, app/_layout.tsx, package.json, and tsconfig.json in the response. These are the default files from the Expo SDK 56 template.
Make your first edit
Now you'll ask Claude to change actual code. Instead of writing the code yourself, you describe what you want and Claude proposes the edit as a diff.
- In the Claude Code prompt box, type the following and press Enter:
Replace the content of app/index.tsx with a simple screen that says 'Daily Habits' in large centered text with a purple background.
Claude proposes the edit as a side-by-side diff. The left side shows the original file. The right side shows the proposed changes.
- Review the diff to see what Claude wants to change.
- Click Accept to apply the changes to your file.
What is a diff?
A diff is a side-by-side comparison showing what changed between two versions of a file. In Claude Code's default permission mode, you always see the diff before any change is applied. You can accept it, reject it, or tell Claude to try something different.
- Start the dev server and open the simulator by running this command in your terminal:
npx expo start
- Press I in the terminal to open the app in the iOS Simulator.
You should see a purple screen with "Daily Habits" displayed in large centered text.
Don't see the purple screen?
Make sure you clicked Accept on the diff in Claude Code. If you missed it, ask Claude the same prompt again.
If the simulator shows an error, copy the full error message and paste it to Claude Code. It can read the error and fix the issue.
help me fix my purple screen not showing up in the simulator
Reject and iterate
Sometimes Claude's first attempt isn't exactly what you want. The reject-and-refine cycle lets you steer Claude toward a better result without starting over.
- In the Claude Code prompt box, type the following and press Enter:
Make the title font bigger and add a subtitle that says 'Track your daily progress'.
- When the diff appears, click Reject. You're going to practice giving Claude more specific instructions.
- Now type a more specific prompt and press Enter:
Actually use fontSize 36 for the title and 16 for the subtitle.
- Review the new diff. Confirm the title uses fontSize: 36 and the subtitle uses fontSize: 16.
- Click Accept to apply the changes.
Why reject and refine?
This reject-and-refine cycle is the basic Claude Code loop. You prompt, review, and either accept or push back with more detail. Being specific about values (like exact font sizes) gives you precise control over the output.
- Check the iOS Simulator. It should automatically refresh with your changes.
- Confirm you see "Daily Habits" in large text (fontSize 36) with "Track your daily progress" below it (fontSize 16), both on a purple background.
Simulator not updating?
If the simulator didn't refresh automatically, press R in the terminal where Expo is running to reload the app.
If you see an error screen, copy the full error text and paste it to Claude Code. It will identify the problem and propose a fix.
help me fix my app after the reject and refine cycle
- Press Ctrl+C in the terminal to stop the dev server.
✔️ Awesome, I've got everything!
Great. Double check you've saved app/index.tsx and confirmed it renders on the simulator before moving on.
ⓧ I'd like to double check the full code
Your app/index.tsx should look similar to this. Claude may have used slightly different variable names or style values, but the key elements are: purple background, centered layout, title with fontSize 36, and subtitle with fontSize 16.
import { View, Text, StyleSheet } from "react-native";
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Daily Habits</Text>
<Text style={styles.subtitle}>Track your daily progress</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#6C63FF",
justifyContent: "center",
alignItems: "center",
},
title: {
fontSize: 36,
color: "#FFFFFF",
fontWeight: "bold",
},
subtitle: {
fontSize: 16,
color: "#FFFFFF",
marginTop: 8,
},
});
You've learned the core Claude Code loop: prompt, review the diff, and accept or reject. Next up, you'll give Claude long-term memory by creating a CLAUDE.md file that shapes every future response.
Write Your CLAUDE.md
You can now prompt Claude Code and review its diffs. But every time you start a new conversation, Claude starts fresh with no memory of your project decisions.
CLAUDE.md solves this. It's a special file that Claude reads at the start of every conversation, giving it persistent knowledge about your project's architecture, conventions, and commands.
In this step, get ready to:
- Create a CLAUDE.md file through Claude Code.
- Test that CLAUDE.md shapes Claude's responses in a new conversation.
- Refine CLAUDE.md with your data model using @-mentions.
Create CLAUDE.md
CLAUDE.md lives at your project root and gets loaded automatically at the start of every Claude Code session. It tells Claude about your tech stack, file structure, naming conventions, and available commands. The more specific your CLAUDE.md, the better Claude's suggestions become.
- Ask Claude Code to generate your project memory file by typing this prompt into the Claude Code panel:
Create a CLAUDE.md file in the project root for a habit tracker app built with Expo SDK 56, React Native 0.85, TypeScript, Expo Router for file-based routing, React Context + AsyncStorage for state, and StyleSheet.create for all styles. Keep it under 100 lines and focus on architecture, conventions, and commands.
What is CLAUDE.md?
Think of CLAUDE.md as a briefing document for Claude. It gives Claude instant context about your project without you having to repeat yourself every conversation.
The official recommendation is to keep it under 200 lines and focus on what matters: build commands, code style, architecture decisions, and workflow rules.
- Claude proposes the file as a diff. Review the contents to make sure it mentions Expo Router, TypeScript, StyleSheet.create(), and AsyncStorage.
- Click Accept to save the file to your project.
- Confirm CLAUDE.md now appears in your project's file explorer sidebar in VS Code.
Don't see CLAUDE.md in the file explorer?
Make sure you clicked Accept on the diff, not Reject. If you accidentally rejected, just send the same prompt again.
If the file was created but you can't see it, click the refresh icon at the top of the Explorer sidebar. Still stuck? Ask Claude Code for help.
Test the difference CLAUDE.md makes
Now that CLAUDE.md exists, Claude will read it every time you start a fresh conversation. Let's prove it shapes Claude's responses.
- Start a new conversation by pressing Cmd+N while the Claude Code panel is focused.
- Type this prompt to test Claude's awareness of your project:
How would you structure the data model for a habit that can be marked complete each day?
- Read Claude's response. It should reference TypeScript types, the architecture from your CLAUDE.md, and patterns like AsyncStorage for persistence.
What should I see?
Without CLAUDE.md, Claude would give a generic answer using whatever framework it guesses. With CLAUDE.md, Claude tailors its response to your exact stack: TypeScript interfaces, React Context for state, ISO date strings, and the conventions you specified.
Refine your CLAUDE.md
Your CLAUDE.md is a living document. As you make project decisions, you add them so Claude remembers them in every future conversation.
- Use an @-mention to give Claude direct access to the file. Type this prompt (the @CLAUDE.md part tells Claude to read the file):
@CLAUDE.md Add a section about our data model: each Habit has an id (string), name (string), and completedDates (string array of ISO date strings). Also add that we use getTodayISO() from utils/date-helpers.ts for date handling.
What are @-mentions?
Typing @ followed by a file or folder name gives Claude direct context about that specific file. It's more precise than asking Claude to "look at" something because it attaches the file contents directly to your prompt.
- Review the diff. You should see a new ## Data Model section added to your CLAUDE.md.
- Click Accept to save the update.
- Open CLAUDE.md in the editor to confirm it now includes your data model with the Habit type and getTodayISO() reference.
Claude added the section in the wrong place?
If the data model section ended up in an odd location, reject the diff and try again with a more specific prompt like: "Add a ## Data Model section after the ## Architecture section." You can always edit CLAUDE.md manually in VS Code too. Help me fix my CLAUDE.md structure.
✔️ Awesome, I've got everything!
Great. Make sure you've saved CLAUDE.md and that it includes both the architecture overview and data model sections.
ⓧ I'd like to double check the full code
Here's what your CLAUDE.md should look like at this point:
# Daily Habits Tracker
## Project Overview
Expo SDK 56 + React Native 0.85 habit tracking app. TypeScript strict mode.
## Architecture
- File-based routing with Expo Router (app/ directory)
- React Context for state management (context/ directory)
- AsyncStorage for local persistence
- Structure: app/ for routes, components/ for shared UI, context/ for providers, utils/ for helpers
## Data Model
- Habit: { id: string, name: string, completedDates: string[] }
- Dates stored as ISO strings (YYYY-MM-DD) via getTodayISO() from utils/date-helpers.ts
## Key Constraints
- Maintain Expo Go compatibility (no custom native modules)
- Minimize Platform.OS branching (prefer shared components)
- Always use StyleSheet.create() (no inline styles)
- TypeScript for all files, strict mode
- Functional components with hooks only
## Conventions
- File naming: kebab-case for files, PascalCase for components
- Named exports for components, default export only for route files (app/ directory)
- Error handling: wrap async operations in try/catch
- Colors: use theme context, never hardcode colors directly in components
## Commands
- Start dev server: npx expo start
- iOS Simulator: press I after npx expo start
- Type check: npx tsc --noEmit
- Install packages: npx expo install <package-name>
Your CLAUDE.md is now your project's brain. Every future conversation with Claude Code starts with this context loaded automatically. Next up, you'll use Plan mode to have Claude architect your entire app structure before writing a single line of code.
Use Plan Mode to Architect the App
Your CLAUDE.md gives Claude persistent memory of your project's conventions. Now it's time to think bigger before you build.
Jumping straight into code without a clear structure leads to wasted effort and rewrites. Plan mode lets Claude propose a full architecture that you review and refine before a single file gets changed.
In this step, get ready to:
- Enter Plan mode and request a full architecture for the habit tracker.
- Review Claude's proposed plan and add feedback.
- Approve the plan and let Claude build the scaffold.
Enter Plan mode
Plan mode is a permission mode where Claude explores your project and proposes changes, but makes zero edits to your files until you approve.
- In the Claude Code panel, click the permission mode indicator at the bottom of the prompt box.
- Select Plan mode from the dropdown.
What is Plan mode?
In Plan mode, Claude describes what it will do and waits for your approval before making changes. It reads your files and thinks through the architecture, but nothing gets written until you say so.
This is the safest way to tackle a big structural decision. You get to see the full picture and give feedback before any code exists.
- Type the following prompt into the Claude Code prompt box and press Enter:
Plan the full file structure and component architecture for our habit tracker. We need: a home screen with a list of habits and completion toggles, an add-habit screen with a form, persistent storage with AsyncStorage, and proper TypeScript throughout.
What does this prompt do?
This prompt asks Claude to design the entire application architecture in one go. Because your CLAUDE.md is loaded automatically, Claude already knows your conventions (file-based routing, React Context, StyleSheet.create, etc.) and will incorporate them into the plan.
Claude will produce a detailed plan describing which files to create, what each one does, and how the pieces connect. VS Code automatically opens the plan as a full markdown document.
Review and comment on the plan
Claude's plan appears as a markdown document in your editor. This is your chance to shape the architecture before any code gets written.
- Read through the proposed file structure and component responsibilities.
- Check that the plan includes a context/ directory for state management, a components/ directory for shared UI, and a utils/ directory for helpers.
- If you disagree with anything (for example, the data layer structure or a component name), add inline comments directly in the plan document.
- Claude sees your feedback and adjusts the plan accordingly.
What to look for in the plan
A good plan for this app should propose files like context/habits-context.tsx, context/theme-context.tsx, components/habit-item.tsx, components/weekly-grid.tsx, and utils/date-helpers.ts. It should also mention updating app/_layout.tsx for the Stack navigator.
Why plan before building?
Without a plan, Claude might solve the wrong problem. It could structure your data layer one way in file A, then contradict itself in file B. Planning up front forces a single coherent design that all files follow.
This plan-first workflow is how professional developers use AI assistants for anything larger than a single-file change.
Approve the plan and let Claude build
Once the plan looks right, you approve it and Claude switches to building mode.
- Click Approve and accept edits at the bottom of the Claude Code prompt.
What happens when you approve?
Approving switches Claude out of Plan mode and into Edit automatically mode (also called acceptEdits). Claude can now create and edit files without asking for permission on each one.
Watch as Claude creates multiple files according to the plan. It will set up the directory structure, create stub files, and update app/_layout.tsx with a Stack navigator.
- Watch Claude create the context/, components/, and utils/ directories with their initial files.
- Confirm the new files appear in the VS Code Explorer sidebar.
You should see these new files and folders in your project:
- context/habits-context.tsx
- context/theme-context.tsx
- components/habit-item.tsx
- components/weekly-grid.tsx
- utils/date-helpers.ts
- app/_layout.tsx (updated with Stack navigator scaffold).
Don't see the new files?
Make sure you clicked Approve and accept edits and not just closed the plan document. Claude only builds after explicit approval.
If Claude seems stuck or stopped partway through, check the Claude Code panel for any error messages. You can prompt Claude with "Continue building the plan" to resume.
help me troubleshoot why Claude didn't create all the files from my approved plan
Your app now has a solid architectural skeleton. Next up, you'll fill in these stubs with real functionality using a faster permission mode.
Build the Core App with Auto-Accept
Your architecture is planned and the scaffold is in place. Now it's time to fill those stub files with real, working code.
In this step, you'll let Claude Code do the heavy lifting. By staying in Edit automatically mode, Claude can create and edit multiple files without stopping to ask permission on every change. You just describe what you want, and Claude builds it.
In this step, get ready to:
- Build the habits context provider with AsyncStorage persistence.
- Build the home screen with a habit list and floating add button.
- Build the add-habit screen and test the app on iOS Simulator.
Build the habits context provider
The data layer is the foundation everything else depends on. Your app needs to store habits, mark them complete, and persist data across restarts.
- Confirm you're still in Edit automatically mode by checking the mode indicator at the bottom of the Claude Code prompt box. It should say Edit automatically from the previous step.
- Type the following prompt into the Claude Code panel:
Build the habits context provider with AsyncStorage persistence. Include the Habit type, a HabitsProvider component, and a useHabits hook that exposes: habits array, addHabit(name), and toggleHabit(id) functions. Use the data model from CLAUDE.md.
What does this prompt do?
This single prompt tells Claude to build three things: a TypeScript interface for the Habit data shape, a React Context provider that loads and saves habits to AsyncStorage, and a custom hook (useHabits) that other components will use to access habit data.
Because your CLAUDE.md already defines the data model, Claude knows each Habit has an id, name, and completedDates array. You don't need to repeat those details in the prompt.
Watch as Claude edits context/habits-context.tsx directly without asking for confirmation. It also creates utils/date-helpers.ts with the getTodayISO() helper referenced in your CLAUDE.md.
Why Edit automatically mode?
In the default mode, Claude shows you a diff for every file change and waits for approval. That's useful when you're learning, but slows you down once you trust the plan.
Edit automatically mode lets Claude create and modify files freely. Since you already approved the architecture in Plan mode, you know the file structure is correct. Now you just need the implementation.
- Once Claude finishes, also prompt it to build the theme context:
Now build the theme context at context/theme-context.tsx. Include a ThemeProvider, useTheme hook, light and dark color palettes, a toggleTheme function, and persist the preference in AsyncStorage. Use purple (#6C63FF) as the primary color for light mode.
Claude creates context/theme-context.tsx with both color palettes and AsyncStorage persistence. You won't see a visible change yet because no screen uses these contexts.
Claude created something unexpected?
If Claude generates code that doesn't match what you expected (wrong file path, missing function, different data shape), tell it specifically what's wrong. For example: "The toggleHabit function should check today's date using getTodayISO() from utils/date-helpers.ts, not Date.now()."
Help me fix my habits context provider
Build the home screen
With the data layer ready, you can build the main screen that displays habits and lets users interact with them.
- Type this prompt into the Claude Code panel:
Build the home screen at app/index.tsx. Show a FlatList of today's habits with circular checkboxes that toggle completion. Add a floating '+' button that navigates to /add-habit. Use the useHabits hook and follow our StyleSheet.create convention. Make it look polished with shadows and rounded cards.
What will Claude build?
Claude replaces the placeholder screen with a full home screen. It creates the HabitItem component in components/habit-item.tsx for the individual habit rows, and wires up app/index.tsx with a FlatList, header showing today's date and progress count, and a floating action button.
Notice you didn't have to mention the HabitItem component by name. Claude inferred that the list needs a reusable item component and created it following the architecture from your CLAUDE.md.
Claude edits multiple files without interrupting you. You won't see the result until you run the app in the next substep.
Build the add-habit screen and test
The last piece is the screen where users type a new habit name and save it. This also requires updating the layout to configure navigation.
- Type this prompt into the Claude Code panel:
Create app/add-habit.tsx as a modal screen. Include a TextInput for the habit name, validation (can't save empty), and a Save button that calls addHabit() then navigates back. Also update app/_layout.tsx to configure the Stack navigator with a purple header and make add-habit a modal presentation.
What does this prompt do?
This prompt asks Claude to do two things at once: build the add-habit form screen AND update the root layout. Claude handles both because it can read the existing _layout.tsx scaffold and knows exactly where to add the screen configuration.
The layout update wraps the app with ThemeProvider and HabitsProvider, making the context available to all screens. Without this wrapping, the useHabits and useTheme hooks would throw errors.
- Start the dev server and open the iOS Simulator by running this command in the VS Code terminal:
npx expo start
- Press I in the terminal to open the app on the iOS Simulator.
- Test the app: tap the + button, type a habit name, tap Save Habit, then tap the habit to toggle its completion.
You should see your habit appear as a card with a circular checkbox. Tapping it fills the circle and adds a strikethrough to the text.
App crashes or shows a red error screen?
Copy the full error message from the red screen and paste it directly into the Claude Code prompt. Claude reads the stack trace, identifies the broken file, and fixes it. This paste-the-error workflow is often faster than debugging manually.
Common causes: a missing import, a hook called outside its provider, or a navigation route name mismatch between _layout.tsx and the actual file name.
Help me debug my Expo app crash
- Press Ctrl+C in the terminal to stop the dev server once you've confirmed the app works.
✔️ Awesome, I've got everything!
Great. Make sure all your files are saved. Your app should have working habit creation and toggle functionality.
ⓧ I'd like to double check the full code
Here's what context/habits-context.tsx should look like. Compare your version with this:
import { createContext, useContext, useState, useEffect, ReactNode } from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { getTodayISO } from "../utils/date-helpers";
export interface Habit {
id: string;
name: string;
completedDates: string[];
}
interface HabitsContextType {
habits: Habit[];
addHabit: (name: string) => void;
toggleHabit: (id: string) => void;
}
const HabitsContext = createContext<HabitsContextType | undefined>(undefined);
const STORAGE_KEY = "daily-habits-data";
export function HabitsProvider({ children }: { children: ReactNode }) {
const [habits, setHabits] = useState<Habit[]>([]);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
loadHabits();
}, []);
useEffect(() => {
if (loaded) {
saveHabits(habits);
}
}, [habits, loaded]);
const loadHabits = async () => {
try {
const stored = await AsyncStorage.getItem(STORAGE_KEY);
if (stored) {
setHabits(JSON.parse(stored));
}
} catch (error) {
console.error("Failed to load habits:", error);
} finally {
setLoaded(true);
}
};
const saveHabits = async (habitsToSave: Habit[]) => {
try {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(habitsToSave));
} catch (error) {
console.error("Failed to save habits:", error);
}
};
const addHabit = (name: string) => {
const newHabit: Habit = {
id: Date.now().toString(),
name,
completedDates: [],
};
setHabits((prev) => [...prev, newHabit]);
};
const toggleHabit = (id: string) => {
const today = getTodayISO();
setHabits((prev) =>
prev.map((habit) => {
if (habit.id !== id) return habit;
const alreadyCompleted = habit.completedDates.includes(today);
return {
...habit,
completedDates: alreadyCompleted
? habit.completedDates.filter((d) => d !== today)
: [...habit.completedDates, today],
};
})
);
};
return (
<HabitsContext.Provider value={{ habits, addHabit, toggleHabit }}>
{children}
</HabitsContext.Provider>
);
}
export function useHabits(): HabitsContextType {
const context = useContext(HabitsContext);
if (!context) {
throw new Error("useHabits must be used within a HabitsProvider");
}
return context;
}
And here's what app/_layout.tsx should look like:
import { Stack } from "expo-router";
import { HabitsProvider } from "../context/habits-context";
import { ThemeProvider, useTheme } from "../context/theme-context";
function RootLayoutContent() {
const { colors } = useTheme();
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: colors.primary },
headerTintColor: colors.headerText,
headerTitleStyle: { fontWeight: "bold" },
contentStyle: { backgroundColor: colors.background },
}}
>
<Stack.Screen name="index" options={{ title: "Daily Habits" }} />
<Stack.Screen
name="add-habit"
options={{ title: "Add Habit", presentation: "modal" }}
/>
<Stack.Screen name="stats" options={{ title: "Weekly Stats" }} />
</Stack>
);
}
export default function RootLayout() {
return (
<ThemeProvider>
<HabitsProvider>
<RootLayoutContent />
</HabitsProvider>
</ThemeProvider>
);
}
If your code looks different, paste the expected version into Claude Code and ask: "Update my file to match this exactly."
Your core app is functional. Next up, you'll learn how to use @-mentions and debugging techniques to polish the UI and fix any rough edges.
Use @-Mentions, Debug Errors, and Iterate on UI
Your core app is running on the simulator with habits, toggles, and persistent storage. That's the foundation.
Now you'll learn three intermediate-to-advanced Claude Code techniques that speed up real development: targeting specific code with @-mentions, debugging errors by pasting them directly to Claude, and iterating on your UI with multi-request prompts.
In this step, get ready to:
- Use @-mentions to point Claude at exact lines of code.
- Debug errors by pasting full error messages to Claude.
- Iterate on your UI with multi-request prompts and build a stats screen.
Use @-mentions for precise context
When you spot something that needs fixing, you could describe the problem in words and hope Claude finds the right file. @-mentions are faster. They let you point Claude at the exact file and lines you're talking about.
- In VS Code, open app/index.tsx and select a block of code that you want Claude to improve (for example, the area around the FlatList or empty state rendering).
- Press Option+K (macOS) or Alt+K (Windows/Linux) to insert an @-mention reference into the Claude Code prompt box.
- Claude Code inserts a reference like @app/index.tsx#15-30 that tells Claude exactly which lines to look at.
- Type your request after the reference. For example: "The habit items need more vertical spacing and the empty state should show a friendlier message. Fix these issues."
- Review the diff Claude proposes and click Accept if the changes look right.
Why @-mentions beat vague prompts
Without @-mentions, Claude searches your entire project to find what you're talking about. With an @-mention like @app/index.tsx#15-30, Claude instantly knows the file, the lines, and the surrounding context. This produces more accurate edits on the first try.
You can also type @ directly in the prompt box followed by a filename. Claude Code supports fuzzy matching, so typing a partial name works too.
Selected text is automatic context
When you highlight code in the editor, Claude can already see it. The prompt box footer shows how many lines are selected. Option+K goes one step further by inserting the file path and line numbers as a permanent reference in your prompt.
Debug with full error messages
Errors happen. The fastest way to fix them with Claude Code is to copy the full error and paste it directly into the prompt box.
- If you see a red error screen in the iOS Simulator (or a terminal stack trace), copy the FULL error message including the stack trace.
- Switch to the Claude Code panel and type a prompt like: "I'm getting this error when I run the app:" followed by pasting the complete error text.
- Claude reads the stack trace, identifies the file and line, and proposes a fix as a diff.
- Review the fix and click Accept to apply it.
Why paste the full error?
Stack traces contain the exact file paths and line numbers where the failure happened. Claude uses these to jump straight to the problem. Paraphrasing an error ("something about undefined") forces Claude to guess. The full text removes the guesswork.
No errors to debug right now?
If your app is running perfectly, great. Keep this paste-the-error technique in your back pocket. You will almost certainly hit a red screen or a type error in later steps. When you do, paste the full output to Claude instead of debugging manually.
help me understand how to debug errors with Claude Code
Iterate on the UI with multi-request prompts
Single-change prompts work, but they're slow when you have a list of improvements. Grouping 2-3 specific requests into one prompt is more efficient because Claude applies them all in a single pass.
- Run your app with npx expo start and press I to verify the current state on the simulator.
- Look at the home screen and note what you'd improve. Then send Claude a multi-request prompt like:
In the home screen: 1) Add a header showing today's date formatted as 'Monday, June 3', 2) Make completed habits have a strikethrough on the text, 3) Add a count showing '3/5 completed' below the header.
- Claude applies all three changes across the relevant files in one pass. Review the diffs and accept them.
What does this prompt do?
Each numbered request targets a specific, concrete change. Claude reads your project structure (thanks to CLAUDE.md) and knows to update app/index.tsx for the header and count, and components/habit-item.tsx for the strikethrough style.
Grouping related requests is efficient, but keep it to 2-3 changes per prompt. More than that and Claude's output quality drops.
Now extend this iteration technique to build a brand new screen. Your home screen already has a stats button (the 📊 icon) that navigates to /stats. Let's have Claude build that screen.
- Ask Claude to build the weekly stats screen by sending this prompt:
Build a stats screen at app/stats.tsx and a WeeklyGrid component at components/weekly-grid.tsx. The stats screen should show: a summary card with today's completion percentage (big number), a 'Last 7 Days' section title, then a WeeklyGrid for each habit. The WeeklyGrid shows the habit name and 7 dots representing the last 7 days - filled with the primary color if completed, border color if missed. Use getLast7Days() from utils/date-helpers.ts. Follow all CLAUDE.md conventions.
- Claude creates both files with StyleSheet.create, theme-aware colors, and the proper data helpers. Accept the diffs.
- Press I in the Expo CLI terminal to reload the app on the simulator.
- Tap the 📊 button on the home screen. You should see the stats screen with a percentage card and weekly grids for each habit.
What did Claude build?
The WeeklyGrid component uses getLast7Days() from your utils/date-helpers.ts to get the last 7 days with ISO date strings and day labels. For each day, it checks if the habit's completedDates array includes that date.
The StatsScreen calculates a completion percentage for today (habits completed divided by total habits) and renders one WeeklyGrid per habit in a scrollable list.
Stats screen showing errors or blank?
Make sure you have at least one habit added. The stats screen shows an empty message if no habits exist.
If you see a red error, copy the full error message and paste it to Claude. Follow the paste-the-error workflow from earlier in this step.
help me fix my stats screen
Verify the full stats screen code matches what you expect.
✔️ Awesome, I've got everything!
Great. Double check you've saved all files, then confirm the stats screen renders in the simulator.
ⓧ I'd like to double check the full code
Here's what app/stats.tsx should look like:
import { View, Text, ScrollView, StyleSheet } from "react-native";
import { useHabits } from "../context/habits-context";
import { useTheme } from "../context/theme-context";
import { WeeklyGrid } from "../components/weekly-grid";
export default function StatsScreen() {
const { habits } = useHabits();
const { colors } = useTheme();
const totalHabits = habits.length;
const today = new Date().toISOString().split("T")[0];
const completedToday = habits.filter((h) =>
h.completedDates.includes(today)
).length;
const percentage = totalHabits > 0 ? Math.round((completedToday / totalHabits) * 100) : 0;
return (
<ScrollView
style={[styles.container, { backgroundColor: colors.background }]}
contentContainerStyle={styles.content}
>
<View style={[styles.summaryCard, { backgroundColor: colors.card }]}>
<Text style={[styles.percentageText, { color: colors.primary }]}>
{percentage}%
</Text>
<Text style={[styles.summaryLabel, { color: colors.textSecondary }]}>
completed today
</Text>
</View>
<Text style={[styles.sectionTitle, { color: colors.text }]}>
Last 7 Days
</Text>
{habits.length === 0 ? (
<Text style={[styles.emptyText, { color: colors.textSecondary }]}>
Add some habits to see your stats here.
</Text>
) : (
habits.map((habit) => (
<WeeklyGrid key={habit.id} habit={habit} />
))
)}
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
padding: 16,
},
summaryCard: {
borderRadius: 16,
padding: 24,
alignItems: "center",
marginBottom: 24,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
percentageText: {
fontSize: 48,
fontWeight: "bold",
},
summaryLabel: {
fontSize: 14,
marginTop: 4,
},
sectionTitle: {
fontSize: 18,
fontWeight: "bold",
marginBottom: 12,
},
emptyText: {
fontSize: 14,
textAlign: "center",
marginTop: 24,
},
});
And here's components/weekly-grid.tsx:
import { View, Text, StyleSheet } from "react-native";
import { Habit } from "../context/habits-context";
import { useTheme } from "../context/theme-context";
import { getLast7Days } from "../utils/date-helpers";
interface WeeklyGridProps {
habit: Habit;
}
export function WeeklyGrid({ habit }: WeeklyGridProps) {
const { colors } = useTheme();
const days = getLast7Days();
return (
<View style={[styles.container, { backgroundColor: colors.card }]}>
<Text style={[styles.habitName, { color: colors.text }]}>
{habit.name}
</Text>
<View style={styles.grid}>
{days.map((day) => {
const isCompleted = habit.completedDates.includes(day.iso);
return (
<View key={day.iso} style={styles.dayColumn}>
<View
style={[
styles.dot,
{
backgroundColor: isCompleted
? colors.primary
: colors.border,
},
]}
/>
<Text style={[styles.dayLabel, { color: colors.textSecondary }]}>
{day.label}
</Text>
</View>
);
})}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
borderRadius: 12,
padding: 16,
marginBottom: 12,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
habitName: {
fontSize: 14,
fontWeight: "600",
marginBottom: 12,
},
grid: {
flexDirection: "row",
justifyContent: "space-between",
},
dayColumn: {
alignItems: "center",
gap: 6,
},
dot: {
width: 24,
height: 24,
borderRadius: 12,
},
dayLabel: {
fontSize: 11,
},
});
You've now unlocked three core techniques: @-mentions for precision, paste-the-error for fast debugging, and multi-request prompts for efficient iteration. Next up, you'll learn how checkpoints let you experiment fearlessly by rewinding changes that don't work out.
Use Checkpoints to Experiment Fearlessly
You have a fully working habit tracker running on the iOS Simulator. But what happens when you want to try something ambitious that might break everything?
Claude Code tracks every change it makes as a checkpoint. If an experiment goes sideways, you can rewind your entire codebase to a known-good state in one click. This unlocks a powerful workflow: try bold ideas, rewind instantly if they fail, then try a different approach.
In this step, get ready to:
- Attempt a major structural refactor that might break the app.
- Rewind to a working state using Claude Code's checkpoint system.
- Try a smaller, scoped approach and confirm it works.
Make a risky change
Big structural refactors are where most projects break. Changing the entire navigation pattern of an app touches every screen and can cascade into dozens of errors.
You're going to intentionally ask Claude for something ambitious to see what happens.
- In the Claude Code panel, type this prompt:
Refactor the entire app to use a tab navigation layout with three tabs: Today, Stats, and Settings. Move the current home screen under the Today tab.
What is this prompt asking for?
This prompt asks Claude to restructure your app's entire navigation from a Stack navigator to a Tab navigator. It's a big structural change because it requires creating new layout files, moving existing screens, and potentially reconfiguring all navigation logic at once.
Claude will start editing multiple files. Since you're in Edit automatically mode, it applies changes without asking for approval on each file.
- Let Claude finish making all its changes.
- Run npx expo start and press I to check the result on the simulator.
You may see errors, a broken layout, or missing screens. That's expected. The refactor touched too many things at once.
Rewind when things break
Claude Code automatically creates a checkpoint before every set of changes. You can jump back to any previous checkpoint to restore your files.
- In the Claude Code panel, scroll up to the message before you sent the tab navigation prompt.
- Hover over that message to reveal the rewind button (⏪).
- Click the rewind button.
- Select Rewind code to here from the options that appear.
What does rewinding do?
"Rewind code to here" reverts all file changes back to the state they were in at that point in the conversation. It keeps your full conversation history intact so you can still see what happened.
Two other options are also available: "Fork conversation from here" (starts a new branch keeping all code changes) and "Fork conversation and rewind code" (new branch with files reverted). For now, the simple rewind is what you need.
- Check your files in the editor. They should be back to their pre-refactor state with your original Stack navigator in app/_layout.tsx.
- Run npx expo start and press I to confirm the app works again.
Your app is back to its fully working state. The failed refactor is gone as if it never happened.
Files still look broken after rewinding?
Make sure you hovered over the message before the refactor prompt, not the refactor prompt itself. The rewind restores to the state at the selected message.
If the Expo dev server was still running during the rewind, stop it with Ctrl+C and restart with npx expo start to pick up the restored files.
Help me rewind my Claude Code checkpoint to restore my working files.
Try a different approach
The full tab-navigation refactor was too broad. A better approach is to scope the change smaller. Your app already has a Stats screen accessible via the 📊 button on the home screen. Instead of restructuring everything, you'll ask for a targeted addition.
- In the Claude Code panel, type this smaller prompt:
Add a Stats tab to the existing navigation but keep the current home screen as-is. The Stats screen should show a simple weekly grid of completed/missed days for each habit.
Why is this prompt better?
This prompt scopes the change to one addition rather than a full restructure. It explicitly says "keep the current home screen as-is" which tells Claude not to touch working code. Smaller prompts that build on what exists produce more reliable results than prompts that tear everything down and rebuild.
- Let Claude process the prompt.
- Run npx expo start and press I to test the app.
- Verify you can still add habits, toggle them, and navigate to the Stats screen using the 📊 button.
If the smaller change works, keep it. If something still breaks, rewind again using the same process and try an even more targeted prompt. This rewind-and-retry cycle is how advanced users iterate fast.
Scope your prompts like pull requests
A good rule of thumb: if a prompt would touch more than 3 files, split it into smaller prompts. Each prompt should be a self-contained change you can verify independently. If it breaks, you only need to rewind one small step.
You now have the safety net to try anything. Next up, you'll unlock Claude Code's most powerful features: Extended Thinking for complex reasoning and Auto mode for fully autonomous building.
Use Extended Thinking and Auto Mode
Your habit tracker is fully functional with multiple screens and persistent storage. Now it's time to unlock Claude Code's most powerful features for tackling complex tasks.
Extended Thinking lets Claude reason through problems before writing code, producing better solutions for multi-file changes. Auto mode takes it further by letting Claude run commands and edit files without any manual approval.
In this step, get ready to:
- Enable Extended Thinking for complex reasoning tasks.
- Use Auto mode for fully autonomous building.
- Manage context by starting fresh conversations.
Enable Extended Thinking
Some tasks require Claude to think deeply before writing code. Designing a complete statistics view with data transformations, layouts, and calculations is one of those tasks.
Extended Thinking gives Claude space to reason through the problem step by step before committing to a solution.
- In the Claude Code panel, click / in the prompt box to open the command menu.
- Toggle on Extended Thinking.
- Type the following prompt into the Claude Code panel:
Design and implement a complete weekly statistics view. It should show the last 7 days as columns, with each habit as a row. Filled circles for completed days, empty for missed. Calculate a completion percentage for the week. Think through the data transformation carefully.
What does Extended Thinking do?
With Extended Thinking enabled, Claude reasons through the problem before writing any code. You'll see a collapsed "thinking" block appear above Claude's response.
Click the thinking block to expand it, or press Ctrl+O to expand all thinking blocks. This is where Claude plans the data model, considers edge cases, and maps out the component structure before producing code.
- Review the diff Claude proposes for your statistics view.
- Click Accept to apply the changes.
- Run npx expo start and press I to verify the stats screen shows the weekly grid with filled and empty circles for each habit.
When to use Extended Thinking
Turn on Extended Thinking for tasks that span multiple files or require design decisions. For simple single-file edits, it adds overhead without much benefit.
Stats screen not showing circles?
Make sure you accepted all of Claude's proposed changes. If some diffs were missed, Claude may have only partially applied the update.
Try asking Claude: "Check if the stats screen and WeeklyGrid component are correctly connected and fix any issues."
Need more help? Why isn't my stats screen rendering the weekly grid correctly?
Use Auto mode for autonomous building
Auto mode is Claude Code's most powerful permission level. Claude can create files, edit code, AND run terminal commands without stopping to ask you for approval.
- Click the permission mode indicator at the bottom of the Claude Code prompt box.
- Select Auto mode.
- Type the following prompt:
Add a dark mode toggle to the app. Create a ThemeContext, add a toggle in the header, persist the preference in AsyncStorage, and update all screens to use theme-aware colors. Run the TypeScript compiler to verify there are no type errors when you're done.
What is Auto mode doing?
Watch the Claude Code panel as Claude works. You'll see it creating context/theme-context.tsx, editing each screen file to use theme colors, and then running npx tsc --noEmit to verify everything compiles cleanly.
A classifier model reviews each command before it runs, providing a safety layer even in Auto mode. If Claude encounters type errors, it reads the output and fixes them automatically.
- Let Claude finish working. Do not interrupt it while it's making changes.
- Once Claude reports that npx tsc --noEmit passed with no errors, run npx expo start and press I to test the app.
- Tap the theme toggle icon in the header. The entire app should switch between light and dark mode.
Claude seems stuck in a loop?
If Claude keeps editing and re-running the compiler without converging, click the Stop button in the Claude Code panel. Switch back to Edit automatically mode (acceptEdits) and give Claude a targeted correction for the specific error.
Need more help? Claude is stuck in a loop fixing type errors in Auto mode. How do I intervene?
Manage context with a fresh conversation
Your conversation is getting long. As Claude's context window fills up, its responses become less focused and it may forget earlier instructions.
- Check the context indicator in the Claude Code prompt box to see how full the context window is.
- Start a fresh conversation by pressing Cmd+N with the Claude Code panel focused.
What carries forward to a new conversation?
Your CLAUDE.md file and Claude's auto-memory carry forward automatically to every new session. Claude still knows your project's architecture, conventions, and data model.
Only the back-and-forth chat history is cleared. This "one task per conversation" habit keeps Claude sharp by giving it full context budget for each new request.
- In the fresh conversation, ask Claude to verify the project by typing this prompt:
Read the project structure and check if there are any inconsistencies between the files. Run tsc --noEmit and fix any type errors.
- Wait for Claude to confirm that tsc --noEmit passes with no errors.
- Run npx expo start and press I to confirm the app still works correctly on the simulator.
Claude doesn't remember my project?
If Claude seems confused about the project structure, make sure CLAUDE.md exists in your project root. Claude reads this file at the start of every new session.
Try asking: "Read CLAUDE.md and summarize the project architecture." If Claude can't find it, you may have accidentally deleted it.
Need more help? Claude doesn't seem to know my project after starting a new conversation
You've now used Claude Code's full power toolkit. Extended Thinking handles complex design tasks. Auto mode builds features autonomously. Fresh conversations keep Claude focused. Next up, you'll put all of these skills together in a final challenge.
Secret mission
Autonomous Feature Building
You've learned every Claude Code technique individually. Now combine them all: Auto mode, Extended Thinking, and your CLAUDE.md working together as Claude builds an entire feature from a single prompt. One prompt. Full autonomy. A complete habit detail screen with edit, delete, streaks, and a 30-day calendar.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- The daily-habits project folder on your Desktop.
- Claude Code VS Code Extension.
- Expo Go installed on the iOS Simulator.
Keep everything running
No action needed. Choose this if you're still actively building or plan to extend the app further.
- Your daily-habits folder stays on your Desktop ready to open any time.
- The Claude Code extension remains in VS Code for future projects.
- Expo Go stays on the iOS Simulator for quick testing.
Pause - I'll come back to this later
Shut down running processes to free up memory. Your files stay on disk.
- If the Expo dev server is still running, press Ctrl+C in the terminal to stop it.
- Quit the iOS Simulator app from the Dock or press Cmd+Q.
- To resume later, open the daily-habits folder in VS Code and run npx expo start again.
Delete - I don't want to use this again
Remove all project resources and start fresh.
- If the Expo dev server is still running, press Ctrl+C to stop it.
- Delete the project folder by running these commands in your terminal:
cd ~/Desktop
rm -rf daily-habits
- Uninstall the Claude Code extension by opening VS Code, pressing Cmd+Shift+X to open Extensions, searching for "Claude Code", and clicking Uninstall.
- Reset the iOS Simulator to remove Expo Go. Open the Simulator app, click Device in the menu bar, then click Erase All Content and Settings.
Nice Work!
Nice Work!
You just went from zero to fully autonomous AI-driven mobile development. You built a complete habit tracker app and mastered the full Claude Code workflow along the way.
You've learned how to:
- Build a complete Expo/React Native habit tracker with multiple screens, persistent storage via AsyncStorage, weekly stats, and dark mode, all running on the iOS Simulator.
- Master Claude Code's progressive workflow: basic prompting and diff review, CLAUDE.md project memory, Plan mode for architecture, permission modes for faster building, and @-mentions for precision.
- Unlock advanced Claude Code features: checkpoints for fearless experimentation, Extended Thinking for complex tasks, Auto mode for fully autonomous building, and context management for long sessions.
- Build an entire habit details feature with a 30-day calendar, streak tracking, edit, and delete using Claude in full autonomous mode.
Ready to quiz yourself?