React Native shares JSX and hooks with React on the web and shares almost nothing else, which is exactly why agents go wrong here: they carry web assumptions across a boundary that looks invisible in the source. An agent needs to know that there is no DOM, that styling is a constrained flexbox subset rather than CSS, and that any dependency containing native code requires a rebuild rather than a Metro refresh. It also needs to know whether the New Architecture is enabled, since that affects which third party libraries work at all.
Save this as AGENTS.md in your project root, and symlink CLAUDE.md to it so Claude Code reads the same rules. Adapt the commands to match your repository before committing it.
# AGENTS.md
This is a cross platform mobile app built with Expo, Expo Router, and TypeScript.
## Project
Stack: Expo SDK 52, React Native, TypeScript (strict), Expo Router, React Query, Zustand for local state, EAS Build and EAS Update.
Layout:
- `app/` Expo Router routes, the directory tree is the navigation tree
- `app/(tabs)/` bottom tab navigator, `app/(auth)/` unauthenticated stack
- `app/_layout.tsx` root providers, `app/+not-found.tsx` fallback
- `src/components/` shared presentational components
- `src/features/<feature>/` screens, hooks, and API calls for one feature
- `src/api/` typed fetch clients and React Query hooks
- `src/native/` thin wrappers around Expo modules (camera, notifications, storage)
- `src/theme/` spacing, colors, typography tokens
- `app.config.ts` app identity, plugins, permissions
- `eas.json` build profiles
Entry points: `app/_layout.tsx`, `app.config.ts`, `eas.json`.
## Commands
```bash
pnpm install
pnpm start # metro bundler
pnpm ios # run on iOS simulator
pnpm android # run on Android emulator
pnpm lint
pnpm typecheck # tsc --noEmit
pnpm test # jest with jest-expo
npx expo install <package> # install a package at the SDK compatible version
npx expo prebuild --clean # regenerate native projects, ask first
npx expo-doctor # diagnose config and version drift
eas build --profile development --platform ios
eas build --profile production --platform all
eas update --branch preview # OTA update, JS only
```
Use `npx expo install` for any package with a native component, not `pnpm add`.
A change to `app.config.ts` plugins, permissions, or a new native dependency requires a new development build. JavaScript only changes reload in the existing build.
## Platform notes
- Test every screen on both an iOS simulator and an Android emulator before calling it done.
- Safe areas: use `useSafeAreaInsets` from react-native-safe-area-context. Do not hardcode status bar height.
- Android back button needs explicit handling on any screen with a modal or a multi step flow.
- Permissions are requested at the moment of use, never on app launch. Both platforms need a usage description in `app.config.ts`.
- Keyboard behavior differs: use `KeyboardAvoidingView` with `behavior="padding"` on iOS and `"height"` on Android.
- Shadows need `elevation` on Android and `shadow*` props on iOS. Use the helper in `src/theme/shadows.ts`.
- Fonts and icons load asynchronously, keep the splash screen up until `useFonts` resolves.
- OTA updates cannot ship native changes. If a change touches native code it needs a store build.
## Code style
- Styles go in `StyleSheet.create` at the bottom of the file. No inline style objects inside render.
- Use `FlashList` or `FlatList` for any list that can exceed ten items. Never map an array into a ScrollView.
- Every list item component is memoized and every list has a stable `keyExtractor`.
- Server state belongs to React Query. Local UI state belongs to component state or Zustand. Do not mirror server data in a store.
- Navigation uses typed routes from Expo Router. No string concatenation for hrefs.
- Native APIs are only called through `src/native/`, so permissions and fallbacks live in one place.
- Spacing and color come from `src/theme/`. No raw hex values or magic numbers in components.
- Images use `expo-image` with an explicit width and height.
## Boundaries
Do not touch without explicit instruction:
- `ios/` and `android/` directories. They are generated by prebuild.
- `app.config.ts` bundle identifier, package name, scheme, version, or build number.
- `eas.json` build profiles and any signing credential.
- `pnpm-lock.yaml`, `.env`, store metadata and screenshots.
Needs human review: new native dependencies, permission additions, changes to push notification handling, and anything affecting deep links.
Version numbers are set by the release process, not by a code change.
## Testing
- `pnpm test` runs Jest with the jest-expo preset.
- Required tests: everything in `src/api/` (request shaping, response parsing), pure helpers, and state reducers.
- Component tests use @testing-library/react-native and cover behavior, not layout.
- Native module wrappers are tested with the Expo module mocked.
- Interaction and layout are verified on device, not in the test suite.
## Git workflow
- Branch from `main`: `feat/short-description`, `fix/short-description`.
- Conventional commits: `fix(onboarding): keep keyboard clear of the submit button`.
- PR description includes screenshots or a screen recording from both iOS and Android.
- State in the PR whether the change is OTA safe or requires a new build.
- Never commit to `main`.
Each of these is worth a line in your rules file, because the model will otherwise repeat it every session.
To react to keyboard visibility, app foreground state, or dimension changes, agents write a useEffect with an interval or a one-shot read, because that pattern is everywhere in web React training data. The platform emits events for all three, and the subscription version is both cheaper and correct. Add to your rules file: 'Use the platform event APIs, Keyboard, AppState, and Dimensions listeners, rather than polling in useEffect, and always return the subscription remove function from the effect.'
Agents produce StyleSheet objects containing grid, position fixed, box-shadow, calc, or a percentage where only a number is accepted. Many of these are silently ignored rather than throwing, so the layout is simply wrong on device and correct in the agent's head. Write: 'Layout is flexbox only. No grid, no position fixed, no CSS shorthand strings. Shadows use shadowColor with shadowOffset and shadowOpacity on iOS plus elevation on Android.'
localStorage, document, window.matchMedia, and DOM event listeners appear regularly in agent output for React Native, usually inside a utility file where nothing signals the platform. These fail at runtime on device rather than at build time. Add: 'There is no DOM and no localStorage. Persistence uses the project's storage library, platform differences use Platform.select, and any code touching window or document is a mistake.'
An agent adds a package containing native modules, restarts Metro, sees the module is null, and then starts debugging the JavaScript. The actual requirement is a pod install on iOS and a fresh native build on both platforms. State it: 'Any dependency with native code requires cd ios && pod install and a full rebuild, not a Metro reload. If a native module resolves as undefined, rebuild before changing any application code.'
Agents map over an array of items inside a ScrollView because it is the shortest working code, which mounts every row at once and makes memory and scroll performance degrade with list length. On a mid-range Android device this is very visible. Add: 'Any list that can exceed roughly twenty items uses FlatList or SectionList with a stable keyExtractor. Never map an unbounded array inside a ScrollView.'
Rather than reading route params, agents put the selected item into Redux or a context so the next screen can read it, which breaks deep links, back navigation, and state restoration because the store and the navigator disagree. Write: 'Screen inputs are passed as route params and read with the navigation hooks. Global state holds server and session data only, never the identity of the currently viewed screen.'
MCP gives the agent access to systems outside your codebase. These are the ones that pay off in a React Native project.
| Server | What it does | Why here |
|---|---|---|
| Filesystem MCP server | Scoped read and write access to the project. | The agent needs to see whether ios/ and android/ exist and are committed before it recommends anything involving native code. |
| Git MCP server | History, diffs, and blame. | Native directory history reveals whether the project maintains its native folders by hand or regenerates them, which changes every native answer. |
| GitHub MCP server | Reads issues, pull requests, and releases from GitHub. | Native module compatibility, especially around the New Architecture, is usually documented only in a library's open issues. |
| Sentry MCP server | Pulls crash reports and stack traces from Sentry. | Native crashes never reproduce in the packager, so the symbolicated device trace is the only real evidence. |
Webapp Testing
Tests local web applications using Playwright for verifying frontend functionality, debugging UI behavior, and capturing screenshots.
artifacts-builder
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui).
great_cto
Claude Code plugin: 7 specialised subagents (tech-lead, senior-dev, qa-engineer, security-officer, devops, l3-support, project-auditor) orchestrating a full SDLC pipeline — architecture, TDD, 12-angle code review, QA, security audit, deploy. 11 project archetypes auto-detected, 13 compliance frameworks (GDPR/PCI-DSS/HIPAA/SOC2/ISO 27001), self-improving knowledge layer that learns from every incident.
iOS Simulator
Enables Claude to interact with iOS Simulator for testing and debugging iOS applications.
Playwright Browser Automation
Model-invoked Playwright automation for testing and validating web applications.
pypict-claude-skill
Design comprehensive test cases using PICT (Pairwise Independent Combinatorial Testing) for requirements or code, generating optimized test suites with pairwise coverage.
Rules files help, but they cannot fix sprawling architecture. A conventional React Native codebase gets more out of an agent than a clever one does.