React Native and Flutter UI Recipes
React Native and Flutter UI recipes are 12 copy-paste prompts for building cross-platform mobile screens with Claude Code, Codex, or Cursor, from adaptive navigation to store preparation. Each prompt pins the package majors current in September 2026 and asks for evidence from both platforms, so you check screenshots and tests instead of reading every line.
This page is for developers who build mobile UI with an agent, and for tech leads who review what it ships. You ask for a swipeable card and get Gesture.Pan(), which react-native-gesture-handler 3 deprecates. You ask for a fast feed and get FlashList’s estimatedItemSize, a prop that FlashList 2 removed. The iOS simulator looks fine, and nobody opens the Android emulator. This page covers the screens; for the stack underneath them (Expo builds and OTA updates, React Native data layers, Riverpod and go_router architecture), start at the mobile development overview.
Which package versions do these recipes assume?
Section titled “Which package versions do these recipes assume?”The agent’s training data spans several majors of every package below, and each major changed an API the agent likes to use. The table shows what npm and pub.dev served on 2026-09-26. Put the majors in AGENTS.md (or CLAUDE.md) so every session inherits them, and let npx expo install or flutter pub add choose exact versions.
| Package | Version | What the agent gets wrong |
|---|---|---|
react-native | 0.87.1 | Old-architecture bridge modules. Current libraries such as Reanimated 4 target the New Architecture. |
@react-navigation/native | 7.4.1 | Mixing the v6 and v7 configuration styles in one navigator. |
@shopify/flash-list | 2.3.2 | estimatedItemSize. Version 2 measures items itself, and the prop no longer exists. |
react-native-reanimated | 4.7.0 | Missing react-native-worklets (peer dependency 0.13.x). Reanimated 4.7 also requires React Native 0.86 to 0.88. |
react-native-gesture-handler | 3.3.0 | Gesture.Pan(), which 3.x marks @deprecated in favour of usePanGesture. The 2.x line lives on npm under the legacy tag. |
expo-image | 57.0.5 | Reaching for react-native-fast-image instead. This page uses expo-image. |
expo-notifications | 57.0.21 | Reaching for @notifee/react-native, whose last npm release is 9.1.8 from 2024-12-20. |
flutter_riverpod | 3.4.3 | StateNotifierProvider from Riverpod 2, which Riverpod 3 moved to legacy.dart. |
go_router | 18.0.1 | Ignoring the SDK floor. Version 18 requires Flutter 3.44 and Dart 3.12. |
Set up the agent once for mobile UI work
Section titled “Set up the agent once for mobile UI work”Two things make every recipe below cheaper to verify: a type check that runs on every edit, and an instruction file that defines “done”. Install your stack’s vendor skills first. The mobile overview has the install commands for the Expo, Flutter, and Callstack React Native skills and for MobileBuildMCP.
Add this block to AGENTS.md, which Codex reads in trusted projects. Claude Code reads it when the repository has no CLAUDE.md (from v2.1.277, latest release channel on 2026-09-26); otherwise copy the block into CLAUDE.md. For Cursor, paste the block into a project Rule.
## Mobile UI: verify before done- Pinned majors: React Native 0.87, React Navigation 7, FlashList 2, Reanimated 4, Gesture Handler 3, expo-image, expo-notifications. Flutter 3.44+, Riverpod 3, go_router 18.- Before using a library API, read its types in node_modules or its pub.dev docs.- Gates: `npx tsc --noEmit`, the linter, and the test runner (React Native); `flutter analyze` and `flutter test` (Flutter).- Launch the app on one iOS simulator and one Android emulator. Save a screenshot of every changed screen to evidence/<task>/ios-*.png and evidence/<task>/android-*.png.- Forbidden in app code: localStorage, window.*, document.*, dart:html, dart:io Platform in widgets.- Never run a store submission, EAS build, or OTA publish.The tools differ in how they enforce that block:
A PostToolUse hook puts type errors in front of Claude on the edit that caused them. Save this as .claude/hooks/mobile-typecheck.sh and make it executable:
#!/usr/bin/env bash# .claude/hooks/mobile-typecheck.sh (PostToolUse, matcher "Edit|Write")set -uo pipefailfile=$(jq -r '.tool_input.file_path // empty')case "$file" in *.ts|*.tsx) out=$(cd "$CLAUDE_PROJECT_DIR" && npx tsc --noEmit 2>&1) \ || { echo "tsc failed after editing $file:" >&2; head -n 40 <<<"$out" >&2; exit 2; } ;; *.dart) out=$(cd "$CLAUDE_PROJECT_DIR" && dart analyze "$file" 2>&1) \ || { echo "dart analyze failed on $file:" >&2; head -n 40 <<<"$out" >&2; exit 2; } ;;esacexit 0Register it in .claude/settings.json:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/mobile-typecheck.sh", "args": [], "timeout": 120 }] } ] }}The matcher filters on the tool name, not on a file glob, so the script filters by extension. On a large project this runs the full type check on every edit, which can take tens of seconds; the timeout of 120 seconds stops a slow run from stalling the session, and adding --incremental to the tsc call lets later runs reuse the previous type information. See Claude Code hooks for the full event list. Before you merge, run /code-review on the branch.
Codex reads the AGENTS.md block in every session. For a repeatable, unattended run of one recipe, use codex exec with the :workspace permission profile (beta in Codex 0.157.1). That profile has no network access and writes only inside the project and the temp directory (it cannot write to $HOME): in our Codex 0.157.1 test on Linux, curl to the npm registry could not resolve the host and a write to $HOME hit a read-only file system. Package installs fail inside the run, and so does anything that needs simulator or emulator state outside the repository. We tested this offline-gate run only with the npm and TypeScript gates; Flutter is untested here, and flutter analyze or flutter test may need a writable SDK directory and $HOME, and so a different profile. Install dependencies first, give Codex only the offline gates, and fail the run unless every gate reports PASS:
npm ci # Flutter (untested here): flutter pub get. The :workspace profile has no networkmkdir -p evidence/feed-listcodex exec -c default_permissions=":workspace" -o evidence/feed-list/summary.md \ "Apply the FlashList recipe in docs/prompts/feed-list.md. Do not install packages. Run the type check, linter and tests from 'Mobile UI: verify before done' in AGENTS.md; skip the simulator, emulator and frame-rate steps. Report each gate on its own line as PASS, FAIL or NOT RUN with the reason."s=evidence/feed-list/summary.mdtest -s "$s" && grep -q PASS "$s" && ! grep -Eq 'FAIL|NOT RUN' "$s" \ || { echo "Codex run incomplete or failing: see $s" >&2; exit 1; }Save the FlashList prompt from this page as docs/prompts/feed-list.md first. The check fails closed: a missing or empty summary, a FAIL, or a NOT RUN stops the script. The device half of the evidence stays with you: launch the app on the iOS simulator and the Android emulator outside Codex, save the screenshots to evidence/feed-list/, and record the frame rates yourself. A green summary.md alone is not device evidence.
Open the pull request and comment @codex review to get a second review of the diff against the same AGENTS.md rules. See Codex code review.
Paste the block into a project Rule so Cursor applies it in every session. For recipes that touch many files (navigation, native modules), start in Plan Mode so you approve the file list before the agent writes code. On the pull request, Bugbot reviews the diff. The Cursor mobile development guide covers the IDE-side loop with simulators.
React Native UI recipes
Section titled “React Native UI recipes”Each recipe states the situation, gives the prompt, and names the evidence that proves the result. Paste a prompt as-is, and edit only the screen names.
Build platform-adaptive navigation with React Navigation 7
Section titled “Build platform-adaptive navigation with React Navigation 7”Your product team wants a bottom tab bar on iOS and a navigation drawer on Android, both backed by the same screens and the same deep links.
Evidence: the deep-link tests, and screenshots of Profile 42 opened by the link on both platforms.
Build a responsive layout for phones, tablets, and foldables
Section titled “Build a responsive layout for phones, tablets, and foldables”Your app stretches a phone layout across a tablet, and the list-detail screens waste two thirds of the display.
Evidence: the three breakpoint tests, and a tablet screenshot in each orientation. Rotation is where hard-coded widths break.
Fix a janky feed with FlashList 2 and expo-image
Section titled “Fix a janky feed with FlashList 2 and expo-image”Your feed holds 1,000+ items with images and stutters when you scroll, and pull-to-refresh jumps the list.
Evidence: before and after frame rates from the same device and scroll, not “should be faster”. The React Native recipe covers the data layer behind the feed.
Show offline state without losing the user’s work
Section titled “Show offline state without losing the user’s work”Field technicians fill in forms without a signal, and today the app shows a spinner forever and drops the entry.
Evidence: the offline-to-online test, and a screenshot of the banner and badge taken in airplane mode. For the storage and sync engine itself (MMKV with TanStack Query, or WatermelonDB for relational sync), use the React Native recipe.
Write a native module when no library covers the hardware
Section titled “Write a native module when no library covers the hardware”Your NFC hardware vendor uses a proprietary tag format that react-native-nfc-manager does not expose, so you need Swift and Kotlin code behind a typed TypeScript API.
Evidence: two clean native builds and the hook tests. CoreNFC does not run on the iOS simulator, so a person scans a real tag on one iPhone and one Android phone before merge. In an Expo app, ask for an Expo module instead; see the Expo recipe.
Native edits need a rebuild, and a stale binary makes a correct change look broken. After any .swift or .kt edit, tell Claude to rebuild the native app: npx expo run:ios / npx expo run:android in an Expo development build, or cd ios && pod install followed by npx react-native run-ios / npx react-native run-android in bare React Native. pod install only syncs dependencies and compiles nothing. Keep npx expo prebuild --clean for a changed config plugin or app.json in a Continuous Native Generation project: it regenerates ios/ and android/ and deletes any direct edits there, so native code in an Expo app belongs in an Expo module. On macOS, MobileBuildMCP lets Claude build, launch the simulator, and read the build log itself.
Run the native work in its own worktree so Xcode and Gradle sync state never mixes with your JavaScript branch. Tell Codex to finish every native edit with the same rebuild: npx expo run:ios / run:android (Expo), or pod install then npx react-native run-ios / run-android (bare React Native). MobileBuildMCP registers with codex mcp add; see the dev-server MCP servers page for the command.
Approve the file list in Plan Mode first: spec, Swift class, Kotlin class, manifest, entitlement, and hook. A missing entitlement file is the most common omission, and it fails only at run time on a device.
Build a swipeable card stack with Reanimated 4 and Gesture Handler 3
Section titled “Build a swipeable card stack with Reanimated 4 and Gesture Handler 3”Your matching screen needs a card stack that follows the finger at the display’s frame rate and never stutters while JavaScript is busy.
Evidence: the gesture tests, plus a screen recording from a physical Android phone, where dropped frames show that the simulator hides. Confirm react-native-worklets is in package.json.
Ask for notification permission at the right moment
Section titled “Ask for notification permission at the right moment”Your app requests notification permission on first launch, most users decline, and a tapped notification opens the home screen instead of the order it mentions.
Evidence: the state-machine tests, and a notification sent to a device and tapped from a killed app. The Expo recipe covers the token pipeline and server side.
Flutter UI recipes
Section titled “Flutter UI recipes”The Flutter recipes use the widget and state APIs of Flutter 3.44+ and Riverpod 3. For package setup, provider architecture, and the go_router auth redirect, follow the Flutter recipe first.
Build a branded widget library that adapts per platform
Section titled “Build a branded widget library that adapts per platform”Every screen styles its own buttons, dark mode is half-finished, and the design team cannot see all variants in one place.
Evidence: passing golden tests in both themes. Goldens catch visual regressions that nobody spots in a diff. Generate them on the same OS as CI, because font rendering differs between macOS and Linux.
Render loading, error, and empty states from Riverpod
Section titled “Render loading, error, and empty states from Riverpod”Your screens show a blank page while data loads and a red error box when the API fails, and each screen handles it differently.
Evidence: the four state tests per screen. The “exactly one fetch” test matters because Riverpod 3 also retries failing providers on its own.
Make Flutter screens feel native on iOS and Android
Section titled “Make Flutter screens feel native on iOS and Android”Your Flutter app uses Material dialogs and switches on iOS, and iOS users notice.
Evidence: the paired platform tests, and one screenshot per platform of the dialog and the date picker.
Keep bottom navigation state with go_router shell routes
Section titled “Keep bottom navigation state with go_router shell routes”Switching tabs resets each tab’s scroll position and navigation stack, and on a tablet the bottom bar looks out of place.
Evidence: the state-preservation test and the two width tests.
Prepare a React Native or Flutter app for store submission
Section titled “Prepare a React Native or Flutter app for store submission”Your app is feature-complete, and the release checklist lives in one person’s head. This recipe turns it into a script that CI can run on every release candidate.
Evidence: a green verify-release.sh run and the checklist signed off by the release owner. Google Play requires new apps and app updates to target Android 16 (API level 36) from 2026-08-31, per the Android developers target-API page checked on 2026-09-26.
Whichever tool prepares the release, the rule is the same: run verify-release.sh in CI on every release branch (the iOS half on a macOS runner), let the script’s exit code decide pass or fail, and have a person sign the checklist before anything reaches a store. The tools differ only in where you review the change. In Claude Code, run the prompt in plan mode (/plan) so the release owner approves the file list, then run /code-review on the branch before you open the pull request. In Codex, start the preparation as a cloud task that opens a pull request with the config changes and RELEASE_CHECKLIST.md, and the release owner approves the version bump and signing configuration there. In Cursor, review the Info.plist, build.gradle, and privacy-manifest changes in one diff before you accept them; a wrong versionCode or a leftover debug entitlement is visible there at a glance.
How do you verify a mobile UI recipe without reading every line?
Section titled “How do you verify a mobile UI recipe without reading every line?”Every recipe above ends with evidence the agent produces and you check. Match the evidence to the kind of change:
| Change | Evidence that proves it | Who signs off |
|---|---|---|
| Layout and adaptive UI | Screenshots on iOS and Android, phone and tablet, both orientations | The developer who opened the task |
| Lists and animations | Frame rates before and after, from the same device and scroll; a physical-device recording | The developer |
| State, navigation, deep links | Unit and widget tests for each state and each link, including cold start | Reviewer of the pull request |
| Native modules | Clean native builds on both platforms, plus a manual run on real hardware | The developer, on a device |
| Release config | A green verify-release.sh and a signed checklist | Release owner or tech lead |
In Claude Code the type-check hook catches wrong APIs on the edit that introduced them; in Codex and Cursor the npx tsc --noEmit / flutter analyze gate in AGENTS.md catches them when the agent runs it before finishing. The tests and screenshots prove behaviour. Put all of it in the pull request with the evidence bundle format, and see mobile app testing patterns for device matrices and flaky tests.
What breaks when agents write React Native and Flutter UI?
Section titled “What breaks when agents write React Native and Flutter UI?”- APIs from an older major. The agent writes
estimatedItemSize,Gesture.Pan(), orStateNotifierProvider. Recovery: keep the version table inAGENTS.md, and tell the agent to read the installed types before it writes library code. In Claude Code the hook catches most of these as type errors; in Codex and Cursor theAGENTS.mdtype-check gate does, when the agent runs it. - Invented or abandoned packages. The agent adds a package that does not exist, or one with no release in years. Recovery: check every new dependency on npm or pub.dev before merge; see dependency verification.
- Web APIs in mobile code.
localStorage,window.innerWidth, ordart:htmlcompile in some setups and crash on a device. Recovery: extend the “Forbidden in app code” line of theAGENTS.mdblock and reject any change without device screenshots. - Stale native binaries. A native change looks broken because Fast Refresh does not rebuild Swift or Kotlin. Recovery: rebuild the binary with
npx expo run:ios/run:android(Expo), orpod installthennpx react-native run-ios/run-android(bare React Native).npx expo prebuild --cleancompiles nothing and deletes direct edits toios/andandroid/. - Simulator-only success. NFC, push, and camera features work only on hardware or need entitlements that the simulator skips. Recovery: name the device test in the recipe’s evidence, and keep one iPhone and one Android phone available for it.
- Gradle and CocoaPods breaks. The agent adds a library that needs a newer Android Gradle Plugin or iOS deployment target. Recovery: paste the full build error back, and ask the agent to state the library’s minimum versions before it changes build files.
Where to go next with mobile UI
Section titled “Where to go next with mobile UI”- Mobile development overview: pick your stack and install its skills
- Expo development patterns: routing, EAS builds, and staged OTA updates
- React Native development patterns: offline data, performance, and CI to the stores
- Flutter development patterns: Riverpod, go_router, Dio, and the check command
- Native iOS and Android recipes: Swift/SwiftUI and Kotlin/Compose with an agent
- Mobile app testing patterns: device matrices, Maestro flows, and platform-specific tests