Skip to content

Mobile app development with Cursor

Mobile app development with Cursor works when the agent can check its own output on a device. A glob-scoped project rule pins React Native versions and the “done” commands. MCP servers let the agent build, launch and screenshot the app on an iOS simulator and an Android emulator. Each change ships with gate results and one screenshot per platform.

This page is for developers and tech leads who build React Native or Expo apps in Cursor. The familiar situation: Agent produced a profile screen that looks right on the iPhone simulator you had open, the pull request says “done”, and QA finds the Android keyboard hiding the save button two days later. Nobody read the 600-line diff closely, and nobody should have to. The fix is a loop in which the agent runs the app on both platforms and hands you evidence.

What a verified mobile loop in Cursor gives you

Section titled “What a verified mobile loop in Cursor gives you”
  • A project rule, .cursor/rules/react-native.mdc, that stops Agent from writing web code or APIs from an older React Native.
  • Two MCP servers in .cursor/mcp.json that give Agent a simulator and an emulator to test on.
  • Four copy-paste prompts: set up the rule, build a screen from a design, fix a platform-only bug, and triage a failing native build.
  • A gate table that says what proves a mobile change and who signs off, so you review screenshots and test output instead of every line.
  • Failure modes specific to mobile work in Cursor, each with a recovery step.

Framework recipes (offline-first data, FlashList, biometrics, EAS builds, over-the-air updates) live in the mobile development cookbook. This page covers the Cursor side: context, the agent loop, and the proof.

Agents mix APIs from several majors because their training data spans them, so the versions belong in the rule, not in your head. The table lists what npm served on 2026-09-26; replace the numbers with the ones in your lockfile.

PackageVersion on npm, 2026-09-26Why the agent needs to know
react-native0.87.1 (Expo SDK 57 pins 0.86.3)The New Architecture is the only architecture since 0.82; old bridge-module tutorials no longer apply
expo57.0.26Add native packages with npx expo install, never by hand-editing versions
@react-navigation/native7.5.0Typed route params come from your RootStackParamList
nativewind4.2.7Styling through className, not StyleSheet.create, if your project uses it
@tanstack/react-query5.104.0Server state and the networkMode options for offline behaviour
mobilebuildmcp2.7.1iOS builds, simulators and tests for the agent (formerly xcodebuildmcp)
@mobilenext/mobile-mcp1.0.6Tap, type, screenshot and read logs on iOS and Android devices

Cursor itself is the 3.x line, checked on 2026-09-26.

Set up Cursor for a React Native repository

Section titled “Set up Cursor for a React Native repository”

Do these four steps once per repository and commit the results, so every teammate’s Agent starts from the same context. If you have never written a Cursor rule, read project rules in Cursor first.

  1. Write a mobile rule scoped to app code. Save it as .cursor/rules/react-native.mdc (the format the project rules page uses). The globs line loads it whenever Agent touches app code, and it costs nothing while Agent works on backend files.

    ---
    description: React Native app conventions and the done criteria for mobile changes
    globs: src/**/*.ts,src/**/*.tsx,app/**/*.tsx
    alwaysApply: false
    ---
    This is React Native 0.86 (New Architecture only) on Expo SDK 57, not React DOM.
    - Never use HTML elements (div, span, img, button), CSS strings or web-only style properties.
    - Add native packages with `npx expo install <pkg>`. Never change a version in package.json by hand.
    - Screens live in src/screens/, one per file. Register each in @src/navigation/RootNavigator.tsx
    and add its params to RootStackParamList in @src/navigation/types.ts.
    - Style with NativeWind className. Shadows: use the boxShadow style prop (RN 0.76+, New Architecture)
    or NativeWind shadow-* classes; never CSS strings or web-only properties.
    - Platform files (.ios.tsx / .android.tsx) take priority over the base file in Metro.
    Before editing a component, check whether a platform variant exists and edit that one too.
    Done means all of these pass, and you paste the output:
    1. npx tsc --noEmit
    2. npx expo lint
    3. npx jest --ci
    4. npx expo install --check (native packages at the versions the SDK expects)
    5. rg -n "<(div|span|img|button|p)[ >]" src app (must print nothing; list only folders that exist)
    6. For UI changes: a screenshot of every changed screen on the iOS simulator and the
    Android emulator, saved to evidence/<task>/ios-*.png and evidence/<task>/android-*.png

    For a bare React Native app without Expo, replace npx expo install with your package manager and npx expo lint with your ESLint command.

  2. Keep committed noise out of the agent’s search. Cursor’s agent code loads .gitignore as well as .cursorignore (checked in @cursor/sdk 1.0.32, 2026-09-26), so gitignored node_modules/, ios/Pods/ and build folders need no second entry. .cursorignore is for files that are committed but that Agent must not read or imitate. Run git ls-files ios/Pods android/app/build | head to see whether your team commits any of them, then list only what shows up:

    Terminal window
    # .cursorignore: committed files the agent must not read or imitate
    # (example entries; keep only paths that git ls-files shows in your repo)
    ios/Pods/
    android/app/src/main/assets/index.android.bundle
    **/__snapshots__/
    src/legacy-web/

    Never ignore ios/Podfile, android/app/build.gradle or app.json: Agent needs them to fix native build errors. Context management in Cursor covers the syntax and the error Agent reports for a blocked path.

  3. Give Agent a device. Add both servers to .cursor/mcp.json in the repository root. MobileBuildMCP (maintained by Sentry) builds and tests iOS apps and needs macOS 14.5 or later with Xcode 16 or later. mobile-mcp (Mobile Next) drives iOS simulators, Android emulators and real devices through screenshots and accessibility snapshots; Android needs the platform tools (adb) on your PATH.

    {
    "mcpServers": {
    "MobileBuildMCP": {
    "command": "npx",
    "args": ["-y", "mobilebuildmcp@latest", "mcp"]
    },
    "mobile-mcp": {
    "command": "npx",
    "args": ["-y", "@mobilenext/mobile-mcp@latest"]
    }
    }
    }

    Both entries follow the vendors’ own Cursor snippets (MobileBuildMCP’s MCP Clients docs page and the mobile-mcp README, checked 2026-09-26). MobileBuildMCP reports its own runtime errors to Sentry; its README links the opt-out. On Linux or Windows, drop MobileBuildMCP and keep mobile-mcp for Android. Each connected server gives Agent more tools to choose from and more descriptions to weigh, so enable them only in the app repository. Set up MCP servers in Cursor shows how to enable, disable and check servers.

  4. Prove the setup before the first real task. Start a new Agent chat and ask: “Use mobile-mcp to list the available devices, then use MobileBuildMCP to build the app for the first iPhone simulator. Report the tool calls you made.” A correct answer names a real device ID and shows a tool call. An answer without a tool call means the server is not connected, and Agent guessed.

Optional: framework skills add version-specific knowledge that loads only when a task needs it. The React Native set from Callstack (community, not Meta) installs for Cursor with the cross-agent skills CLI (1.7.0 on npm, 2026-09-26; see installing and managing skills): npx skills@latest add callstackincubator/agent-skills --skill react-native-best-practices --agent cursor -y. The mobile cookbook lists the Expo and Flutter sets.

Build a screen from a design: plan, implement, prove

Section titled “Build a screen from a design: plan, implement, prove”

Screen work goes wrong in the plan, not in the typing. Start in Plan Mode, approve a plan that names files and tests, then switch to Agent mode to implement against the gates. Agent modes in Cursor explains when to switch.

Point the prompt at your existing components with @ so Agent uses their real props instead of inventing new ones. If the design lives in Figma, building frontend UI from designs shows the Figma MCP side; the prompt below works from a written spec.

Review the result in this order, and stop at the first failure:

  1. The file list. Run git diff --stat. A screen task touches the screen, its test, the navigator and the types file. Anything under ios/ or android/ means a native change you did not ask for: ask Agent why before going further.
  2. The gate output. All five commands pass, and the web-element search printed nothing.
  3. The screenshots. Two images with the keyboard up. The bio field is visible on both, and the save button is reachable.
  4. The tests. Open the test file only. It is the spec; if it asserts what you asked for, the implementation behind it does not need a line-by-line read.

If a step fails, restore the checkpoint from before the implementation turn rather than stacking fixes on a bad base. See checkpoints and branching.

Handle iOS and Android differences without two codebases

Section titled “Handle iOS and Android differences without two codebases”

Most platform differences are values, not structures. Pick the smallest tool that expresses the difference, because every platform file is a second place a later fix has to land.

The differenceUseWhy
A value: android_ripple, status-bar style, padding, font weight, header optionPlatform.select() inlineOne file, and the difference is visible in review
A component whose behaviour differs (date picker, haptics, permissions flow)Component.ios.tsx and Component.android.tsx beside a shared props typeMetro loads the platform file first; the shared type keeps both variants in step
Something React Native has no API forA native module (Expo Modules API or a Turbo Module)Needs a rebuild and a reviewer who knows Swift or Kotlin; see native iOS and Android recipes

The “before” screenshots matter as much as the “after” ones. A fix verified only after the change cannot show that Agent reproduced the bug you reported rather than a different one.

Debug Metro, native build and device-only failures

Section titled “Debug Metro, native build and device-only failures”

Mobile failures often sit outside the TypeScript code: in Metro’s cache, CocoaPods, Gradle or a missing rebuild. Give Agent the full log and a rule about what it may change, because the fastest way to make a build error disappear is to upgrade or remove a dependency, and that is rarely the fix you want.

The usual first moves, which Agent should try before editing any code:

SymptomFirst command (terminal)
Unable to resolve module for an installed packagenpx expo start --clear (Expo) or npx react-native start --reset-cache (bare)
iOS build fails after adding a native packagenpx expo prebuild --clean (Expo prebuild) or cd ios && pod install (bare)
Android build fails after adding a native packagecd android && ./gradlew clean, then rebuild
The JavaScript change works but a native change does not appearRebuild the app; a Metro reload does not load native code

For crashes that happen only on a device or in a release build, the React Native cookbook covers release-build debugging with React Native DevTools, and bug hunting in Cursor covers the general reproduce-first loop.

How do you verify mobile changes without reading every line?

Section titled “How do you verify mobile changes without reading every line?”

Make Agent produce evidence, and review the evidence. The rule’s “Done means” list already forces most of it; this table says what each layer catches and who owns it.

LayerCommand or artifactCatchesSigned off by
Types and lintnpx tsc --noEmit, npx expo lintWrong props, wrong navigation params, unused codeCI
Web-code searchrg -n "<(div|span|img|button|p)[ >]" src appHTML elements, which tsc does not reject in a React Native projectCI
Dependency alignmentnpx expo install --check (Expo)Native packages at versions the SDK does not expectCI
Unit testsnpx jest --ci with React Native Testing LibraryBehaviour you wrote as acceptance criteriaDeveloper who ran the task
Device evidenceOne screenshot per changed screen per platform in evidence/<task>/Layout, keyboard, safe-area and platform-only bugsDeveloper who ran the task
End-to-end flowsMaestro flows in .maestro/ (see mobile app testing patterns)Broken navigation, deep links and auth gatesCI, on every pull request
Native changesgit diff --name-only | grep -E '^(ios|android)/' prints somethingUnrequested native edits that need a rebuild and a native reviewerTech lead or native owner
ReleaseStaged store rollout, and for Expo a fingerprint check before any OTA updateA bad build reaching every userTech lead

In the web-code search, list only the folders your repository has: rg exits with code 2 on a missing path such as app, and the gate then fails for the wrong reason. On a CI runner without ripgrep, install it or run grep -rnE with the same pattern.

Put the gates that do not need a device into CI, so a green pull request already means types, lint, the web-code search and the unit tests passed; CI/CD pipelines with Cursor shows the workflow. Keep the screenshots in the pull request description, and use the evidence bundle format if your team has adopted it. The reviewer then reads what changed in behaviour, not the diff.

What breaks when Cursor’s agent works on a mobile app?

Section titled “What breaks when Cursor’s agent works on a mobile app?”

Agent writes web code. Symptom: <div>, onClick, className strings with web-only utilities, or style={{ cursor: 'pointer' }} in a screen. Cause: the rule did not load: its globs miss the folder Agent edited, or its frontmatter is malformed. Recovery: fix the globs or frontmatter, start a new chat, confirm with the canary check from project rules, and keep the web-element search in CI so the next slip fails the build.

The fix lands in the wrong platform file. Symptom: Agent edited DatePicker.tsx, the tests pass, and nothing changes on the device. Cause: DatePicker.ios.tsx exists and Metro loads it first. Recovery: ask Agent to list platform variants with ls src/components/DatePicker*, apply the fix to each, and keep the platform-file line in the rule.

A native change without a rebuild. Symptom: Agent reports the feature works, but the screenshot shows the old behaviour or a red “native module not found” screen. Cause: Metro reloaded JavaScript only. Recovery: rebuild through MobileBuildMCP or npx expo run:android, and require a fresh screenshot after the rebuild.

Agent “fixes” a build by changing versions. Symptom: the build is green and package.json or the lockfile changed. Recovery: restore the checkpoint, rerun the build-triage prompt with its no-version-change rule, and keep the npx expo install --check gate in CI so a mismatched native package fails loudly.

Only one platform was tested. Symptom: the summary has screenshots from one simulator. Cause: Agent used whatever device was booted. Recovery: reject the result and ask for the missing platform; the “Done means” list names both, so this is a rule violation you can point to.

The MCP server finds no device. Symptom: an empty device list or “no booted simulator”. Recovery: boot a simulator in Xcode or an emulator in Android Studio, check adb devices for Android, and ask Agent to list devices again. On a machine without Xcode, MobileBuildMCP cannot build iOS at all; use mobile-mcp with Android and a teammate’s Mac or CI for iOS.

Navigation types drift. Symptom: tsc errors on navigation.navigate('Profile', …) after a new screen. Cause: Agent registered the screen but did not update RootStackParamList. Recovery: @-mention src/navigation/types.ts in the prompt; the rule already names it, so repeat the prompt in a new chat if the rule did not load.

Where to go next with mobile development in Cursor

Section titled “Where to go next with mobile development in Cursor”