Skip to content
Tutorial··14 min

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.

Build a React Native app with Claude Code — from zero to TestFlight
Building a React Native app with Claude Code is genuinely fast — faster than any web project, because mobile boilerplate is repetitive and the agent handles it well. 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) and internal testing (Android) Prerequisites -------------- - macOS (required for iOS builds), Node.js 18+ - Xcode 15+ (for iOS simulator and TestFlight) - Android Studio (for Android emulator, optional) - Expo account (free at expo.dev) - Supabase account (free tier) - Claude Code installed Step 1 — Create the Expo project ---------------------------------- npx create-expo-app HabitTracker --template blank-typescript cd HabitTracker claude Install the React Native skill: claude skills add react-native-mobile This loads context about Expo, React Navigation, and the Expo SDK — saves you from explaining these patterns every session. 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 Claude Code sometimes gets wrong 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. we're using Expo SDK 51. 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 (causes crashes — 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 thing the agent sometimes misses: 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: push notifications don't work in the iOS simulator. You need a physical device or Expo Go to test them. Ask Claude Code to add a dev-only mock mode: > add a isDev check — in dev mode, log the notification payload to the console instead of scheduling it, so we can test the settings screen flow without a physical device. Step 7 — Build for TestFlight ------------------------------- First, configure your Expo app credentials: npx eas login npx eas build:configure Then build: npx eas build --platform ios --profile preview The --profile preview flag builds for internal testing (TestFlight) without requiring a full store review. This is the fastest path to sharing with beta testers. Once the build completes (~15 minutes), Expo will give you a link to download the .ipa. Upload it to App Store Connect: xcrun altool --upload-app -f your-build.ipa -t ios -u your@apple.com -p your-app-specific-password 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 ------------------------------------------------- - Expo SDK version compatibility (it knows which packages need which SDK version) - 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.