Claude Code can write most of a React Native screen in a minute. Whether that code is shippable depends far less on the prompt than on the project around it. Give it a CLAUDE.md that describes your Expo stack and its rules, restrict what it's allowed to run, add hooks that type-check and lint every edit, work in small planned tasks, and review the output against a mobile-specific checklist. That setup turns "it generated something" into "it generated something we can merge".
AI coding agents are very good at React Native in general and surprisingly bad at your React Native app in particular. They don't know you use Expo Router instead of React Navigation stacks, that native packages must be installed with npx expo install, that your Supabase tables need RLS, or that a web-only library will break the Android build. Left to guess, they guess like the average of the internet.
This guide is the setup we use to stop the guessing.
1. Start with a CLAUDE.md that describes your mobile stack
Claude Code reads a CLAUDE.md file at the root of your project at the start of every session. It's the single most effective place to improve output quality, because every instruction in it applies to every task.
Run /init inside Claude Code to generate a first draft from your codebase, then rewrite it with the things the code can't tell it. A good React Native CLAUDE.md covers four things: the stack, the commands, the rules and the "never" list.
# Project: Expo + Supabase mobile app
## Stack
- Expo SDK (managed workflow) with development builds, TypeScript strict mode
- Expo Router for navigation (file-based, in app/). Do not add React Navigation stacks manually.
- NativeWind for styling. Use className, not StyleSheet, unless animating.
- Supabase for auth, Postgres and storage. Client lives in src/lib/supabase.ts.
- TanStack Query for server state. No Redux.
## Commands
- Install a package: npx expo install <pkg> (never plain npm install for native packages)
- Type-check: npx tsc --noEmit
- Lint: npx expo lint
- Project health: npx expo-doctor
- Unit tests: npm test
## Rules
- Every string rendered on screen must be inside <Text>.
- Lists longer than ~20 items use FlatList, never .map() inside ScrollView.
- Screens read params with useLocalSearchParams and fetch by ID; never pass whole objects as params.
- New Supabase tables: enable RLS in the same migration and add select/insert/update/delete policies using (select auth.uid()).
- Any new permission (camera, photos, location) needs a clear purpose string in app.json.
- Check behavior on both iOS and Android; call out platform differences in your summary.
## Never
- Never use web-only libraries (anything that touches window, document or the DOM).
- Never put the Supabase service role key or any secret in app code or EXPO_PUBLIC_ variables.
- Never edit files in ios/ or android/ directly; use app.json or config plugins.
- Never run eas build, eas submit or eas update.
Keep it short enough to read in a minute. Long, vague instruction files get followed less reliably than short, specific ones. When Claude Code makes the same mistake twice, that's a line missing from this file.
You can also add CLAUDE.md files in subfolders, such as supabase/CLAUDE.md with your migration conventions. They're picked up when Claude Code works in that part of the project.
2. Decide what it's allowed to run
Claude Code asks for permission before running commands, and you can pre-approve the safe ones and block the risky ones in .claude/settings.json, committed to the repo so the whole team shares it:
{
"permissions": {
"allow": [
"Bash(npx tsc --noEmit)",
"Bash(npx expo lint)",
"Bash(npx expo-doctor)",
"Bash(npx expo install:*)",
"Bash(npm test:*)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Bash(eas submit:*)",
"Bash(eas update:*)",
"Bash(npx supabase db push:*)"
]
}
}
The allow list removes friction for checks you always want run. The deny list is about blast radius: an agent shouldn't be able to read your secrets, ship an over-the-air update to production users or push a migration to your live database. Those stay human decisions.
3. Make every edit prove it compiles
Hooks run shell commands automatically at points in Claude Code's workflow. A PostToolUse hook on file edits gives the agent immediate feedback, so type errors get fixed in the same turn instead of piling up:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "npx tsc --noEmit --pretty false 2>&1 | head -n 30" }
]
}
]
}
}
On a large project, a full type-check after every edit can be slow. In that case, run a linter or formatter on the edited file in the hook, and keep the full tsc and expo-doctor run as the final step of each task, listed in CLAUDE.md.
4. Work in small, planned tasks
The biggest quality jump doesn't come from settings; it comes from task size. "Build the whole onboarding flow" produces a large diff that's hard to review and easy to get subtly wrong. Break it down:
-
Plan first. Use plan mode (Shift+Tab cycles modes in Claude Code) and ask for a plan: which files change, which new routes appear in
app/, which tables and policies are needed. Correct the plan before any code is written. This is where you catch "I'll add React Navigation" before it happens. - One slice at a time. Data layer and migration first, then one screen, then the next. Each slice ends with the checks passing.
- Ask for the summary you need. End tasks with: "Summarize what changed, anything platform-specific, and anything you weren't sure about." The "not sure" list is where to look first.
For tasks you repeat, save the prompt as a custom slash command in .claude/commands/. For example, .claude/commands/new-screen.md:
Create a new screen for: $ARGUMENTS
- Add the route file under app/ following our Expo Router structure.
- Use NativeWind classes and our components in src/components.
- Fetch data with TanStack Query through src/lib/api.
- Handle loading, empty and error states.
- Run npx tsc --noEmit and npx expo lint before finishing.
- Summarize the files you changed.
Now /new-screen order history with pull-to-refresh produces screens that look like the rest of your app.
5. Review AI-generated React Native code like a mobile engineer
Compiling isn't the bar. These are the problems that pass type-checking and still hurt in production. Review each diff for them:
Rendering and performance
-
.map()over long lists instead ofFlatList(or FlashList), and missing stablekeyExtractors. - Inline functions and objects in list items that defeat memoization on big lists.
- Effects that fetch on every render because a dependency is a new object each time.
Platform behavior
- Shadows written for iOS only (Android needs elevation).
- Forms without
KeyboardAvoidingView, so the keyboard covers the input. - Content under the notch or home indicator because safe areas were skipped.
- Back behavior on Android that doesn't match the navigation.
Data and security
- Supabase queries that rely on the client to filter data instead of RLS.
- New tables or storage buckets without policies.
- Secrets in
EXPO_PUBLIC_variables, which are bundled into the app. - Permissions requested on launch instead of when the feature is used.
Dependencies
- New packages installed with
npm installinstead ofnpx expo install, which can pull versions incompatible with your SDK. - Web-only packages that build on iOS and then crash on Android, or vice versa.
- A new native module added without noting that it needs a new development build (and can't ship as an OTA update).
6. Test on a device, not just in the terminal
Generated UI often looks right in the code and wrong on a phone. Before merging any screen:
- Run it in a development build on a real iPhone and a mid-range Android.
- Try it with a large font size (accessibility settings) and a small screen.
- Go offline and back online.
- For critical flows (sign-in, checkout), add an end-to-end test with a tool like Maestro, so regressions from future generated changes get caught automatically.
Claude Code can help write those tests too; ask for them as part of the same task, not as an afterthought.
A setup checklist
-
CLAUDE.mdwith your stack, commands, rules and "never" list. -
.claude/settings.jsonwith safe commands allowed and secrets, deploys and production migrations denied. - A type-check or lint hook on every edit.
- Plan mode for anything bigger than one screen.
- Custom commands for repeated tasks like new screens and migrations.
- A mobile review checklist applied to every diff.
- Device testing on iOS and Android before merge.
FAQ
Is Claude Code good at React Native? It's strong at TypeScript, React and common React Native patterns. Most problems come from missing project context, like which router, styling and backend you use, which is exactly what CLAUDE.md provides.
Can it run the app? It can run commands like the type-checker, linter, tests and the Expo dev server, but you still need to look at the app on a device. Visual and platform-specific bugs need human eyes.
Should it write Supabase migrations? It can draft them well, but review every policy before applying, and keep production database pushes out of its permissions.
Does this work with other AI coding tools? The ideas do: project rules, restricted commands, automatic checks, small tasks and a review checklist. The file names and settings formats differ by tool.
Conclusion
The difference between AI-generated React Native code you throw away and code you ship is mostly setup: tell the agent how your app works, limit what it can touch, make it prove every edit compiles, keep tasks small, and review with mobile-specific eyes.
What's in your CLAUDE.md for React Native? Share the one rule that saved you the most time in the comments.
Top comments (1)
Found these tips useful, thx. One catch: your tsc hook exits with head's 0, so Claude never sees the errors. Exit 2 with the output on stderr and it gets fed back