How I let Claude Code build my RN screens without it breaking the safe area every time
My actual Sonnet 4.6 setup for shipping Expo apps solo: a screen-builder agent, a platform reviewer that nags about iOS vs Android, and a Detox smoke test on stop.
I build mobile apps alone. No designer, no QA, no backend team to argue with. Just me, a MacBook, a Pixel 7 on my desk and an old iPhone SE that I keep around specifically because if a layout survives that tiny screen it survives anything. For about a year I treated AI coding tools as a fancy autocomplete and not much else. Then I sat down and actually wired Claude Code into the way I work on React Native, and now it builds whole screens for me while I watch the simulator.
This is the exact setup I run, called Mobile RN Studio in my notes. Model is Sonnet 4.6, not Opus. People in mobile keep reaching for the biggest model out of habit and then complain about the bill. UI work is high edit volume, lots of small repetitive changes to JSX and styles, and Sonnet keeps up fine for a fraction of the cost. My runs land around 28 cents. I will take that.
What actually lives in the config
Three things do the heavy lifting: my CLAUDE.md rules, three subagents, and two hooks. The MCP servers I keep boring on purpose: filesystem, github, and playwright (I use Playwright to drive the Expo web build for quick visual checks before I touch a device). Here is the memory file, trimmed to the parts that matter.
# Mobile RN Studio
Stack: Expo SDK 53, React Native, TypeScript, expo-router, NativeWind.
## Hard rules
- Respect platform conventions. iOS and Android are NOT the same OS.
Use Platform.select for anything that differs (shadows, ripple, headers).
- No screen ships without safe-area handling. Wrap in SafeAreaView or
use useSafeAreaInsets. Never hardcode a 44px top pad and call it done.
- Keep the bundle in check. No moment.js, no lodash full import,
no random animation lib when Reanimated already ships. Justify every dep.
## Conventions
- Screens live in app/, components in components/, never inline 200-line JSX.
- StyleSheet.create or NativeWind classes, no inline style objects in render.
- Every Pressable needs an accessibilityLabel.
## Before you say done
- Run the typecheck (the hook does it, but check it passed).
- Confirm it renders on iOS AND Android, not just one.The safe-area rule is in there because I got burned. Early on Claude would happily build a beautiful header that sat right under the iPhone notch on every device with a notch. Looked perfect in the Android emulator, totally broken on real hardware. Now it is the second line of the file and it mostly behaves.
The three subagents
I keep a tiny team. A screen-builder that writes the actual UI, a platform-reviewer whose entire job is to catch iOS versus Android divergence before I do, and a tester that handles Detox. The platform-reviewer is the one that earns its keep. Below is its definition.
---
name: platform-reviewer
description: Reviews RN screens for iOS vs Android divergence and safe-area bugs. Use after any screen is built or edited.
tools: Read, Grep, Glob
---
You review React Native code for cross-platform correctness. You do NOT write features.
Check every changed screen for:
- Hardcoded top/bottom padding that ignores safe-area insets
- Shadow props (iOS) used without elevation (Android), or vice versa
- TouchableOpacity ripple assumptions that only work on one platform
- Platform-specific APIs called without Platform.OS guards
- Fonts/letterSpacing that render differently and need Platform.select
Output a short numbered list of findings, each with file:line and the fix.
If a screen is clean on both platforms, say so in one line. Do not pad.It runs read-only on purpose. I do not want my reviewer also being my editor, because then it starts justifying its own work. Separate context, separate job. The screen-builder writes, the reviewer pokes holes, and I make the call.
Hooks that catch the dumb stuff
Two hooks. A PostToolUse typecheck so a broken edit never sits quietly, and a Stop hook that runs a Detox smoke test so I find out a screen is blank before I commit, not after.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx tsc --noEmit -p tsconfig.json"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "detox test -c ios.sim.debug e2e/smoke.test.ts --silent"
}
]
}
]
}
}The Detox smoke test is dead simple. It launches the app, taps through the three main tabs, and asserts each one has its title text on screen. That is it. It is not full coverage, it is a smoke alarm. Catches the white-screen-of-death where a screen throws on mount and the bundler does not warn you.
That is a real shape of a session. The reviewer caught the exact two things I would have caught myself an hour later on the Pixel, except it caught them in eleven seconds. The elevation one specifically. I would have shipped a card that has a nice shadow on iOS and looks completely flat on Android, and I would not have noticed until a user emailed me.
What I reach for, what I skip
| Task | Let Claude run it | Do it myself |
|---|---|---|
| New screen from a sketch | Yes, screen-builder nails layout | |
| Cross-platform style review | Yes, the reviewer is faster than me | |
| Reanimated gesture handlers | Sometimes, verify on device | Tricky ones |
| Native module / config plugin | Almost always me | |
| App Store metadata + screenshots | Me, every time |
Native modules are still my job. The moment you are editing a config plugin or touching Podfile stuff, the model starts confidently inventing API that does not exist in your SDK version. I let it draft, I never let it ship that unread.
12:36If you have never set up a subagent, that Net Ninja video is the cleanest intro I have seen. It is web-focused but the mechanics are identical to what I do for mobile. Define the markdown file, give it a tight tool allowlist, point it at one job.
Create custom subagents - Claude Code DocsThe frontmatter spec I copied for my platform-reviewer. The tool allowlist field is the important part.code.claude.comdisler/claude-code-hooks-masteryWhere I figured out the Stop hook lifecycle. My Detox-on-stop idea came straight out of reading this.github.com4.2kA few things that took me a while to learn
- Keep the reviewer read-only. The second it can edit, it stops being a critic.
- Detox on stop, not on every edit. Smoke tests are slow, you do not want one per keystroke.
- Put the safe-area rule in CLAUDE.md, near the top. It gets ignored if it is rule number nine.
- Test on the smallest real device you own. Sims lie about safe-area sometimes.
- Pick the cheap model for UI. You will run it ten times more often, so cost compounds.
That is the whole thing. Nothing exotic, just a model that fits the work, three small agents with one job each, and two hooks that catch the mistakes I always make. If you want to try the exact setup, grab it with setuproll install claude-code-mobile-rn and point it at an Expo project. Then go put it on a real phone, because that is the only review that counts.