Build a React Native app with Claude Code — from zero to TestFlight
A real walkthrough: use Claude Code to build, test, and deploy a React Native app with Expo, navigation, and a Supabase backend. Covers the mobile-specific gotchas that trip up web developers.
By CB Patel
Mobile boilerplate is repetitive, which makes it a good fit for Claude Code. But mobile development has its own set of gotchas: native dependencies, simulator quirks, code signing, and the App Store review process. This walkthrough covers all of it.
What we're building
A habit tracker app:
- Home screen: today's habits with checkboxes
- Progress screen: streak and completion chart
- Settings: add/remove habits, set reminders
- Expo push notifications
- Supabase backend for persistence and auth
- Deployed to TestFlight (iOS)
Prerequisites
- Node.js
- macOS and Xcode (for the iOS Simulator)
- Android Studio (for Android emulator, optional)
- Expo account (free at expo.dev)
- A paid Apple Developer account (required to submit to App Store Connect and TestFlight)
- Supabase account (free tier)
- Claude Code installed
Step 1 — Create the Expo project
npx create-expo-app HabitTracker --template blank-typescript
cd HabitTracker
Before starting Claude Code, install the React Native skill into the project:
curl -fsSL --create-dirs -o .claude/skills/react-native-mobile/SKILL.md https://claudeskil.com/skills/react-native-mobile/SKILL.md
This gives Claude Code context about Expo, React Navigation, and the Expo SDK, loaded whenever a request matches — saves you from explaining these patterns every session. Then start Claude Code:
claude
Step 2 — Set up navigation
install @react-navigation/native, @react-navigation/bottom-tabs, and the required Expo dependencies (expo-linking, expo-constants, react-native-screens, react-native-safe-area-context). Create a bottom tab navigator with three tabs: Today (HomeScreen), Progress (ProgressScreen), and Settings (SettingsScreen). Create placeholder screen components for each. Handle the safe area properly on iOS.
A critical detail on Expo: the Expo dependency versions must align with the SDK version. If you see peer dependency warnings, ask:
fix any peer dependency version conflicts with npx expo install --fix, so every package matches our Expo SDK version.
Step 3 — Today screen with habits
create the TodayScreen. It should: 1) fetch today's habits from a local array (we'll connect Supabase later), 2) render each habit as a row with a checkbox, the habit name, and the current streak number, 3) tapping the checkbox marks the habit complete for today, updating local state, 4) completed habits move to a "Done" section below. use StyleSheet for styling, not inline styles.
Review the component carefully. Two common issues: the checkbox state not persisting on re-render (needs useState or useReducer, not a plain variable) and the "Done" section creating a FlatList nested inside a ScrollView — use one or the other, not both.
Step 4 — Supabase integration
Install:
npx expo install @supabase/supabase-js
create lib/supabase.ts — a Supabase client configured for React Native. Use AsyncStorage from @react-native-async-storage/async-storage for session persistence instead of localStorage. Create tables: habits (id, user_id, name, color, created_at) and completions (id, habit_id, user_id, completed_date). Add RLS policies so users only see their own data.
One React Native–specific detail: Supabase needs a custom storage adapter for React Native since localStorage doesn't exist. Verify the supabase.ts file uses AsyncStorage:
import AsyncStorage from '@react-native-async-storage/async-storage';
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
auth: { storage: AsyncStorage, autoRefreshToken: true, persistSession: true }
});
If it used localStorage or omitted the storage option, ask it to fix that explicitly.
Step 5 — Auth (magic link)
add a sign-in screen using Supabase magic link auth. The screen should have an email input and a "Send magic link" button. On success, show "Check your email" message. Handle the deep link callback — when the user taps the link in their email, the app should open and exchange the token for a session. Use expo-linking for the deep link handling. Add the app scheme to app.json.
Deep linking on Expo has a specific setup. Ask Claude Code to:
update app.json to add scheme: "habittracker". Update the Supabase redirect URL in the auth screen to use Linking.createURL('auth/callback'). Create a useAuthDeepLink hook that listens for incoming URLs and calls supabase.auth.exchangeCodeForSession.
Step 6 — Push notifications
install expo-notifications. Create a lib/notifications.ts module that: 1) requests permission on first run, 2) gets the Expo push token, 3) stores it in the Supabase profiles table, 4) schedules a daily reminder at a user-configurable time. Add a notification time picker to the Settings screen.
Note: you can test push notifications on a physical device, on an Android Emulator with Google Play services, or on an iOS Simulator running on Xcode 14 or later.
Step 7 — Build for TestFlight
First, install the EAS CLI, log in, and configure the project:
npm install --global eas-cli
eas login
eas build:configure
TestFlight needs a store build, so build with the production profile, then submit it:
eas build --platform ios --profile production
eas submit --platform ios
The eas submit step uploads the build to App Store Connect for TestFlight (you need the paid Apple Developer account for this step). eas build --platform ios --auto-submit combines both commands.
Ask Claude Code to write the TestFlight release notes:
write TestFlight release notes for v0.1.0 of a habit tracker app with daily check-ins, streak tracking, and push reminders.
Mobile-specific things Claude Code handles well
- Navigation patterns (when to use stack vs. tab vs. modal)
- Platform-specific conditionals (Platform.OS === 'ios' checks)
- StyleSheet.create() patterns vs. inline styles
- FlatList vs ScrollView vs SectionList trade-offs
Things to double-check yourself
- Native module setup (any package requiring npx pod-install or manual Xcode config)
- Code signing configuration in eas.json
- App Store metadata (screenshots, privacy manifest, age rating)
- App Transport Security settings for custom API endpoints
Explore more React Native and Flutter skills at claudeskil.com/category/ios-developer.